Wie kann ich zwei Bash-Befehle in -exec des Befehls find verwenden?

32

Ist es möglich, 2 Befehle im -execTeil des findBefehls zu verwenden?

Ich habe versucht, etwas wie:

find . -name "*" -exec  chgrp -v new_group {}  ; chmod -v 770 {}  \;

und ich bekomme:

find: fehlendes Argument für -exec
chmod: Kein Zugriff auf {}: Keine solche Datei oder Verzeichnis
chmod: Kein Zugriff auf;: Keine solche Datei oder Verzeichnis

Luc M
quelle

Antworten:

44

Für den findBefehl können Sie auch einfach mehrere -execBefehle hintereinander hinzufügen :

find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;

Beachten Sie, dass dieser Befehl in seinem Ergebnis der Verwendung entspricht

chgrp -v neue_gruppendatei && chmod -v 770 datei

auf jeder Datei.

Alle die find‚s Parameter wie -name, -exec, -sizeund so weiter, sind tatsächlich testet : findweiterhin einen nach dem anderen , so lange laufen , wie die gesamte Kette bisher bewertet wurde wahr . Daher wird jeder nachfolgende -execBefehl nur ausgeführt , wenn die vorherigen true zurückgegeben haben (dh den 0Status der Befehle verlassen). Versteht aber findauch logische Operatoren wie oder ( -o) und nicht ( !). Um eine -execTestkette unabhängig von den vorherigen Ergebnissen zu verwenden, müsste man also etwa Folgendes verwenden:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
rozcietrzewiacz
quelle
4
+1: Ja, das ist der eleganteste Weg, es zu tun. Wenn Sie erklären können, warum Sie verwenden '{}'(Apostrophe um die Klammern), besuchen Sie bitte: unix.stackexchange.com/q/8647/4485
Benutzer unbekannt
1
@user Ich weiß leider nicht, ob es noch nötig ist. Ich habe gerade einen Test gemacht und bin nicht auf eine Situation gestoßen, in der sich etwas ändern würde. Ich denke, es ist nur "gute Praxis", die aussterben wird.
Rozcietrzewiacz
2
Die Anführungszeichen sind wichtig für Dateien mit Leerzeichen im Namen.
Naught101
17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
Glenn Jackman
quelle
@Gilles: Die Wunder der -cseltsamen Handhabung von 0 $ lassen mich denken, dass dies jedes Mal falsch ist, wenn ich es mir anschaue , aber es ist definitiv richtig.
Derobert
Ich mag die explizite Shell definiert ...
Djangofan
Diese Antwort (und Giles 'Antwort) scheint die bessere Antwort für die gestellte Frage zu sein sh -c.
14

Ihr Befehl wird zuerst von der Shell in zwei durch ;ein Trennzeichen getrennte Befehle zerlegt, was einer neuen Zeile entspricht:

find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;

Wenn Sie einen Shell-Befehl ausführen möchten, rufen Sie eine Shell explizit mit auf bash -c(oder sh -cwenn es Ihnen egal ist, dass die Shell speziell bash ist):

find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;

Beachten Sie die Verwendung von {}als Argument für die Shell. Es ist das nullte Argument (normalerweise der Name der Shell oder des Skripts, aber das spielt hier keine Rolle) "$0".

Sie können mehrere Dateinamen gleichzeitig an die Shell übergeben und die Shell durchlaufen lassen. Dies ist schneller. Hier übergebe ich _als Skriptnamen und die folgenden Argumente sind Dateinamen, über die for x(eine Abkürzung für for x in "$@") iteriert wird.

find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +

Beachten Sie, dass Sie seit Bash 4 oder in zsh hier nichts mehr finden müssen. Führen shopt -s globstarSie in bash den Befehl aus (fügen Sie ihn in Ihren Ordner ein ~/.bashrc), um das **/Stehen für ein rekursives Verzeichnis glob zu aktivieren . (In zsh ist dies die ganze Zeit aktiv.) Dann

chgrp -v new_group -- **/*; chmod -v 770 -- **/*

oder wenn Sie möchten, dass die Dateien der Reihe nach durchlaufen werden

for x in **/*; do
  chgrp -v new_group -- "$x"
  chmod -v 770 -- "$x"
done

Ein Unterschied zum findBefehl besteht darin, dass die Shell Punktdateien (Dateien, deren Name mit a beginnt .) ignoriert . Um sie in die Bash aufzunehmen, setzen Sie zuerst GLOBIGNORE=.:..; Verwenden Sie in zsh **/*(D)als Glob-Muster.

Gilles 'SO - hör auf böse zu sein'
quelle
Diese Antwort (und Glenns Antwort) scheint die bessere Antwort für die gestellte Frage zu sein sh -c.