Ich habe Probleme, einen Dateinamen zu zitieren, der schließlich an übergeben wird chmod
. Das Skript wurde ursprünglich für SSHs StrictModes
und 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 chmod
beschwerte 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, find
dass weitergegeben wird chmod
? Oder wie bekomme ich chmod
den 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
fwiw, find [options] -print0
verarbeitet in Verbindung mit (piped to) xargs -0 [options]
Dateinamen mit Leerzeichen, ohne Schleifen for
oder 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'
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. IFS
ist 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 IFS
Trick 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
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...\b
da, aber nicht \t
? Naiv dachte ich, \b
sei entweder die Tabulatortaste oder die Rücktaste. Ich habe die Rücktaste irgendwie verstanden, aber die Glocke macht für mich keinen Sinn.\0
; siehe Mac OS X Lion: Was sind ungültige Zeichen für einen Dateinamen? auf Superuser. Ein Vorsprung \0
ist wahrscheinlich eine gute Möglichkeit für einen Virus, sich zu verstecken ...find
kann mit Leerzeichen in Dateinamen umgehen. Ihr for loops
verursacht die Probleme. Sie können Ihre Logik in mehrere find
Befehle 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 find
Aussagen verwenden, um so zu find
tun, 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 {} +
Shared
oder $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 read
Schleife 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 -r
verhindert, 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 -q
sagt, grep
nichts auszugeben, aber sein Exit-Status wird nur erfolgreich sein, wenn es mindestens eine Übereinstimmung gefunden hat. Und da if
nur 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...
Fahrrad
find | xargs sudo chmod
anstatt alles in einer for-Schleife aufzurollen, in der Leerzeichen Sie dazu bringen, über die Variable zu iterieren.