MacOS – Quote a filename in a script passed to chmod

bashcommand linemacosunix

I'm having trouble quoting a filename that's eventually passed to chmod. The script was originally written for SSH's StrictModes and authorized_keys, but I had to expand it due to a bad UMASK that caused some filesystem discomfort. The script is below, and its producing boatloads of:

chmod: /Users/<user>/Library/Application: No such file or directory
chmod: Support/TextMate/Managed/Bundles/HTML.tmbundle/Macros/Delete: No such file or directory
chmod: whitespace: No such file or directory
chmod: between: No such file or directory
chmod: tags.plist: No such file or directory
...
chmod: /Users/<user>/Library/Caches/com.operasoftware.Opera/Media: No such file or directory
chmod: Cache/index: No such file or directory
cut: stdin: Illegal byte sequence
...

I've tried a few workarounds, but none of them have helped. I tried double quoting like ""$file"", but the issue persisted. I also tried the back ticks (that are discouraged in this case), but the issue persisted.

The closest I got to actually quoting was the dysfunctional "\"""$file""\"". But then chmod complained the filename (with quotes) was not a real file:

$ sudo ~/fix-perms.sh
chmod: "/Users/Shared/.localized": No such file or directory
chmod: "/Users/<user>/.CFUserTextEncoding": No such file or directory
chmod: "/Users/<user>/.lesshst": No such file or directory

How do I quote the filename that comes out of find that gets passed onto chmod? Or how do I get chmod to take the filename as a single argument?


$ cat ~/fix-perms.sh 
#!/bin/bash

# Directories
find /Users/* -type d -exec chmod 0700 {} \;
find /Users/Shared -type d -exec chmod 0777 {} \;

# Files
for file in `find /Users/* -type f`;
do
    if [ `file "$file" | cut -d":" -f 2 | grep -i -c executable` -eq 0 ];
    then
        `chmod 0600 "$file"`
    else
        `chmod 0700 "$file"`
    fi
done

for user in `ls -A /Users`;
do
    if [ -e "/Users/$user/.ssh/authorized_keys" ];
    then
        chmod 0600 "/Users/${user}/.ssh/authorized_keys"
    fi
done

Best Answer

fwiw, find [options] -print0, when used in conjunction with (piped to) xargs -0 [options], handles filenames with spaces, without messing with for loops or IFS:

find /Users ! -path '/Users/Shared*' -type f -print0 | xargs -0 -I {} \
    bash -c 'if [[ "$(file -b --mime-type -- "{}")" = "application/x-mach-binary" ]]; then
                 chmod 700 "{}"
             else
                 chmod 600 "{}"
             fi'