Einen Dateinamen in einem an chmod übergebenen Skript zitieren?

Ich habe Probleme, einen Dateinamen zu zitieren, der schließlich an übergeben wird chmod. Das Skript wurde ursprünglich für SSHs StrictModesund geschrieben authorized_keys, aber ich musste es aufgrund eines Fehlers erweitern UMASK, der einige Unannehmlichkeiten im Dateisystem verursachte. Das Skript ist unten und es produziert Bootsladungen von:

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
...

Ich habe ein paar Workarounds ausprobiert, aber keiner hat geholfen. Ich habe versucht, doppelte Anführungszeichen wie ""$file"", aber das Problem bestand weiterhin. Ich habe auch die Back-Ticks ausprobiert (von denen in diesem Fall abgeraten wird), aber das Problem blieb bestehen.

Am nächsten kam ich tatsächlich dem Zitieren, war die dysfunktionale "\"""$file""\"". Aber dann chmodbeschwerte sich, dass der Dateiname (mit Anführungszeichen) keine echte Datei war:

$ 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

Wie zitiere ich den Dateinamen, der sich daraus ergibt, finddass weitergegeben wird chmod? Oder wie bekomme ich chmodden Dateinamen als einzelnes 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
Interessieren Sie sich für Bash-Scripting oder gibt es andere Optionen, die Sie akzeptieren würden? Wenn Sie Bash sind, kann ich möglicherweise Dinge überarbeiten, die sein sollen, find | xargs sudo chmodanstatt alles in einer for-Schleife aufzurollen, in der Leerzeichen Sie dazu bringen, über die Variable zu iterieren.

Antworten (4)

fwiw, find [options] -print0verarbeitet in Verbindung mit (piped to) xargs -0 [options]Dateinamen mit Leerzeichen, ohne Schleifen foroder 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'
danke @patrix, das ist besser als das, was ich mir ausgedacht hätte.
Ja, es gibt so viele Möglichkeiten, Dinge mit der Shell zu tun ... (ich finde es manchmal frustrierend).
Leichte Alternative: find /path -type d -maxdepth 1 ! -perm 0700 -exec chmod 0755 {} \; Das -maxdepth #ist willkürlich, manchmal muss oder will man nicht zu weit nach unten gehen.

Ich konnte das Problem lösen, indem ich den Dateinamen IFSänderte IFS=$(echo -en "\n\b")und ihn nicht anführte. IFSist Internal Field Separator , wird (unter anderem) zum Aufteilen von Wörtern nach Shell-Erweiterungen verwendet und enthält standardmäßig ein Leerzeichen.

Ich habe den IFSTrick bei nixCrafts BASH Shell gefunden: For Loop File Names With Spaces .

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

SAVED_IFS=$IFS
IFS=$(echo -en "\n\b")

# Directories
chmod 0755 /Users
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

IFS=$SAVED_IFS
Ich kann diese Methode nicht wirklich empfehlen, da sie bei Dateinamen mit Zeilenvorschüben (oder Glocken? warum ist das da ...) fehlschlägt. Angenommen, ein Benutzer hat eine Datei mit dem Namen erstellt a\n*\nb– sie wird in drei Wörter zerlegt: „/Users/something/a“, * und „b“; dann wird das * in eine Liste von Dateien und Unterverzeichnissen in dem Verzeichnis, in dem Sie sich befinden, erweitert, und das Skript ändert dann ihre Berechtigungen (möglicherweise falsch, da die Unterverzeichnisse nicht ausführbar sind). Aber Ihre Benutzer werden dies wahrscheinlich nicht tun. Wahrscheinlich...
@Gordon - Ich habe die Shell-Metazeichen nicht nachgeschlagen, wollte es aber. Danke, dass du mir von der Glocke erzählt hast... Warum um alles in der Welt ist \bda, aber nicht \t? Naiv dachte ich, \bsei entweder die Tabulatortaste oder die Rücktaste. Ich habe die Rücktaste irgendwie verstanden, aber die Glocke macht für mich keinen Sinn.
@Gordon - anscheinend verwendet die Bash-Seite eine falsche Annahme für OS X. Unter OS X unter HFS + können Dateinamen enthalten \0; siehe Mac OS X Lion: Was sind ungültige Zeichen für einen Dateinamen? auf Superuser. Ein Vorsprung \0ist wahrscheinlich eine gute Möglichkeit für einen Virus, sich zu verstecken ...

findkann mit Leerzeichen in Dateinamen umgehen. Ihr for loopsverursacht die Probleme. Sie können Ihre Logik in mehrere findBefehle einfügen. Nicht jedes Verzeichnis im Home-Ordner eines Benutzers sollte nur ihm selbst zugänglich sein. Öffentlich und Sites kommen mir in den Sinn. Sie können sie separat in einem anderen Suchbefehl beheben.

find /Users/* ! -path '/Users/Shared*'  -type d ! \( -name Public -o -name Sites \) -exec chmod 0700 {} +

Der Befehl schließt das Shared-Verzeichnis zusammen mit den Verzeichnissen Public und Sites aus und ändert dann die Berechtigungen für die verbleibenden Verzeichnisse.

+Sie können in Ihren findAussagen verwenden, um so zu findtun, als ob es wäre xargs.

find /Users/Shared -type d -exec chmod 0777 {} +

Angenommen, die ausführbaren Dateien im Home-Ordner eines Benutzers sind Anwendungspakete, dann könnten Sie dies tun.

find /Users/* ! -path '/Users/Shared*' type f ! perm -755 -exec chmod 0600 {} +
Danke fd0. "find kann mit Leerzeichen in Dateinamen umgehen. Ihre for-Schleifen verursachen die Probleme." - OK, gut, ich nehme dich beim Wort. Was jetzt? Du sagst mir nicht wirklich, wie ich das beheben soll. (Machen Sie sich keine Sorgen um Sharedoder $HOME/Public. Das ist das i-Tüpfelchen. Das Hauptanliegen sind Berechtigungen für Home-Verzeichnisse).

Die Lösung von mtklr (und Patrix) wird funktionieren, aber ich finde es einfacher, eine while readSchleife zu verwenden, wenn es um eine Dateiliste von geht find ... -print0:

find /Users '!' -path '/Users/Shared*' -type f -print0 |
    while IFS= read -d '' -r file; do
        if file "$file" | grep -iq ": .*executable"; then
            chmod 700 "$file"
        else
            chmod 600 "$file"
        fi
    done

read -d ''verwendet Nullen als Trennzeichen. Ich habe auch -rverhindert, dass es versucht, Escapes zu analysieren, und IFS=um zu verhindern, dass Leerzeichen am Ende des Dateinamens abgeschnitten werden (und da es sich um ein Präfix für den Befehl handelt read, gilt es nur für diesen und Sie müssen es nicht zurücksetzen nachher).

Übrigens, ich habe auch verwendet if ... | grep -q ...- das -qsagt, grepnichts auszugeben, aber sein Exit-Status wird nur erfolgreich sein, wenn es mindestens eine Übereinstimmung gefunden hat. Und da ifnur der Exit-Status des darin enthaltenen Befehls getestet wird, ist das alles, was benötigt wird.

Übrigens basiert dies hauptsächlich auf BashFAQ #20: Wie kann ich Dateinamen finden und sicher handhaben, die Zeilenumbrüche, Leerzeichen oder beides enthalten? . Ich empfehle mal reinschauen...