CD in alle Verzeichnisse kopieren, Befehl für Dateien in diesem Verzeichnis ausführen und zum vorherigen aktuellen Verzeichnis zurückkehren

41

Ich versuche, ein Skript zu schreiben, das in einem bestimmten Verzeichnis mit vielen Unterverzeichnissen auf einer Ebene ausgeführt wird. Das Skript cd in jedes der Unterverzeichnisse, führt einen Befehl für die Dateien im Verzeichnis aus und cd out, um mit dem nächsten Verzeichnis fortzufahren. Wie geht das am besten?

Etwas Jones
quelle
1
Angesichts des derzeitigen Informationsstands sehe ich keine Relevanz für youtube-dl.
HalosGhost

Antworten:

83
for d in ./*/ ; do (cd "$d" && somecommand); done
Psusi
quelle
12
Da der Anrufbeantworter also eine Erklärung ausgelassen hat, werde ich es versuchen. for d in ./*/Startet eine Schleife, in der jedes Element ./*/(in diesem Fall eine Liste von Dateien / Ordnern) in einer Variablen gespeichert wird $d. do (cd "$d" && somecommand);startet den Körper der Schleife. Innerhalb des Körpers startet es eine Subshell und führt die Befehle cdund aus somecommand. Da es sich um eine untergeordnete Shell handelt, behält die übergeordnete Shell (die Shell, von der aus Sie diesen Befehl ausführen) das CWD und andere Umgebungsvariablen bei. doneschließt einfach den Schleifenkörper.
Qix
Diese Methode funktioniert für Unterverzeichnisse von Verzeichnissen:, for d in ./*/ ; do (cd "$d" && ls); donewird nicht funktionieren. aber for d in ./*/ ; do (cd "$d" && for d in ./*/ ; do (cd "$d" && ls); done ); donewird funktionieren. -verwendet ls als Befehl in diesem Beispiel.
Michael Dimmitt
-bash: cd: ./*/: No such file or directory
S. Tarık Çetin
15

Der beste Weg ist, überhaupt nicht zu verwenden cd:

find some/dir -type f -execdir somecommand {} \;

execdirist wie exec, aber das Arbeitsverzeichnis ist anders:

   -execdir command {} [;|+]
          Like   -exec,   but  the  specified  command  is  run  from  the
          subdirectory containing the matched file, which is not  normally
          the  directory  in  which  you  started  find.  This a much more
          secure  method  for  invoking  commands,  as  it   avoids   race
          conditions  during resolution of the paths to the matched files.

Es ist kein POSIX.

muru
quelle
Funktioniert das mit Aliasen? Ich habe eine zum Herunterladen bestimmter Dateien, aber sie wird nicht erkannt, wenn ich find * / .link -type f -execdir md $ (cat .link) {} \ eingebe.
Etwas Jones
@SomethingJones Nein, findführt diese Befehle aus, sodass keine Aliase erkannt werden. Was ist mdund ist das .linkein Verzeichnis?
muru
.link ist eine Textdatei mit der URL, die zum Herunterladen benötigt wird. md ist ein Alias, der mit einer Reihe von gesetzten Flags gespielt werden soll. Gibt es eine Möglichkeit, auf Aliase aufmerksam zu machen?
Etwas Jones
@SomethingJones für Ihren speziellen Anwendungsfall, in bash: find . -type f -iname '*.link' -execdir ${BASH_ALIASES[md]} -i {} \;Sie brauchen nicht zu tun catmit wget, was einen hat -iin einer URL - Flag aus einer Datei zu lesen. Auch dies unterscheidet sich ein wenig von Ihrer ursprünglichen Frage (da Sie anscheinend nur an den genannten Dateien interessiert sind .linkund nicht an anderen Dateien, die möglicherweise vorhanden sind).
muru
Weißt du, wie man das mit zsh macht? Ich habe versucht, was Sie mir gegeben haben, und erhalte die Fehlermeldung "Bad substitution". Wie kann ich den Inhalt der .link-Datei extrahieren? Ich weiß, dass ich es in diesem Fall nicht brauche, aber ich kann mir vorstellen, dass ich es bald tun werde.
Etwas Jones
2
cd -P .
for dir in ./*/
do cd -P "$dir" ||continue
   printf %s\\n "$PWD" >&2
   command && cd "$OLDPWD" || 
! break; done || ! cd - >&2

Der obige Befehl muss keine Subshells ausführen - er protokolliert lediglich den Fortschritt in der aktuellen Shell, indem er $OLDPWDund abwechselt $PWD. Wenn Sie cd -die Shell austauschen, ändert sie im Grunde genommen den Wert dieser beiden Variablen, während sie die Verzeichnisse wechselt. Es druckt auch den Namen für jedes Verzeichnis, wie es dort zu stderr arbeitet.

Ich habe es mir nur ein zweites Mal angesehen und festgestellt, dass ich mit der Fehlerbehandlung besser umgehen kann. Es überspringt ein Verzeichnis, in das es nicht kann, cdund cdgibt eine Meldung aus, warum stderr ausgeführt werden muss, und es weist breakeinen Exit-Code ungleich Null auf, wenn die commandAusführung nicht erfolgreich ist oder wenn die commandMöglichkeit, in das ursprüngliche Verzeichnis zurückzukehren, auf irgendeine Weise beeinträchtigt wird - $OLDPWD. In diesem Fall wird auch ein cd -letzter - und der resultierende aktuelle Arbeitsverzeichnisname in stderr geschrieben.

mikeserv
quelle
1
for D in ./*; do
    if [ -d "$D" ]; then
        cd "$D"
        run_something
        cd ..
    fi
done
Kirill
quelle