Eine Aktion durchführen, die in jedem sub-directory mit Bash
Arbeite ich an einem Skript, dass eine Aktion durchführen muss in jedem Unterordner eines bestimmten Ordners.
Was ist der effizienteste Weg, dass zu schreiben?
- Bitte berücksichtigen Sie kommen wieder durch und neubewerten Antworten für die Richtigkeit-Sie haben eine akzeptierte Antwort, immer eine Menge von Ansichten, die trotz major-bugs (f/e, es läuft über ein Verzeichnis, in dem jemand zuvor lief
mkdir 'foo * bar'
verursachenfoo
undbar
zu der Iteration, auch wenn Sie nicht existieren, und die*
ersetzt werden, mit einer Liste aller Dateinamen, auch nicht-Verzeichnis sind). - ...noch schlimmer ist, wenn jemand lief
mkdir -p '/tmp/ /etc/passwd /'
-- wenn jemand führt ein Skript nach dieser Praxis auf/tmp
zu, sagen Sie, finden Sie die Verzeichnisse zu löschen, könnten Sie am Ende das löschen/etc/passwd
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
find
params-wenn Sie möchten, rekursive oder nicht-rekursive Verhalten.find . -mindepth 1 -type d
mkdir 'directory name with spaces'
in vier separate Wörter auf.-mindepth 1 -maxdepth 1
oder es ging zu tief.for D in `find . -type d`; do //Do whatever you need with D; done
Einer version, die verhindert, dass das erstellen einer sub-Prozess:
Oder, wenn die Aktion ist ein einzelner Befehl, das ist übersichtlicher:
Oder eine noch prägnantere version (danke @enzotib). Beachten Sie, dass in dieser version bei jedem Wert von
D
wird ein trailing slash:if
oder[
mit:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
oder eine Kombination der beiden neuesten:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
stattfor D in *; do
.Einfachsten nicht rekursiv Weg ist:
Den
/
am Ende sagt, verwenden Sie nur Verzeichnisse.Es gibt keine Notwendigkeit für
shopt -s dotglob
zu gehören dotfiles/dotdirs bei der Expansion von wildcards. Siehe auch: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html/*
statt*/
mit/
für den Pfad, den Sie verwenden möchten.*/
würde die Unterverzeichnisse von der aktuellen LageVerwenden
find
Befehl.In GNU
find
verwenden, können Sie-execdir
parameter:oder mit
-exec
parameter:oder mit
xargs
Befehl:Oder mit für Schleife:
Für recursivity versuchen extended globbing (
**/
) statt (aktivieren durch:shopt -s extglob
).Weitere Beispiele finden Sie unter: Wie gehen Sie auf jedes Verzeichnis und einen Befehl ausführen? bei SO
-exec {} +
ist POSIX-angegeben,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
ist eine weitere rechtliche option, und ruft weniger Muscheln als-exec sh -c '...' {} \;
.Praktische Einzeiler
Option A ist korrekt für Ordner mit Leerzeichen dazwischen. Auch im Allgemeinen schneller, da Sie nicht drucken, jedes Wort in einen Ordner, der als Namen eine separate Einheit.
find . -type d -print0 | xargs -0 -n 1 my_command
my_command
.Dadurch wird eine subshell (was bedeutet, dass die Variablenwerte gehen verloren, wenn die
while
Schleife wird beendet):Diese nicht:
Entweder wird funktionieren, wenn es Leerzeichen im Verzeichnis-Namen.
find ... -print0
undwhile IFS="" read -r -d $'\000' dir
-d ''
ist weniger irreführend über bash-syntax und die Fähigkeiten, da-d $'\000'
impliziert (fälschlicherweise), dass$'\000'
ist in gewisser Weise anders''
-- in der Tat, man könnte leicht (und auch fälschlicherweise) folgern daraus, dass die bash unterstützt Pascal-style-strings (Länge angegeben ist, in der Lage, NUL Literale) anstelle von C-strings (NUL getrennt, nicht enthalten NULs).Könnten Sie versuchen:
oder ähnliches...
Erklärung:
find . -maxdepth 1 -mindepth 1 -type d
:Nur Verzeichnisse finden mit maximal rekursive Tiefe von 1 (nur die Unterverzeichnisse von $1) und minimaler Tiefe von 1 (schließt den aktuellen Ordner
.
)cd "$f"
. Es funktioniert nicht, wenn die Ausgabe vonfind
ist die string-split, so müssen Sie die getrennte Teile des namens als separate Werte in$f
machen, wie gut Sie tun oder nicht zitieren$f
's-Erweiterung strittig.find
's Ausgabe ist zeilenorientiert (ein name, der auf eine Linie, in der Theorie-aber siehe unten) mit dem Standard --print
Aktion.find -print
ist nicht sicher Weg, um pass beliebigen Dateinamen, da kann man etwas laufenmkdir -p $'foo\n/etc/passwd\nbar'
und erhalten Sie ein Verzeichnis mit/etc/passwd
als eine separate Zeile in Ihrem Namen. Umgang mit Namen von Dateien in/upload
oder/tmp
Verzeichnisse ohne Sorgfalt, ist ein guter Weg, um privilege-escalation-Angriffe.find ... -exec
ist sicher: die übergebenen Argumente auf einen argv-Liste werden einzeln durch Kündigung beendet NULs).( tempdir=$(mktemp -d "$PWD"/tempdir.XXXXXX) && cd "$tempdir" && { mkdir 'directory with spaces' $'directory\nwith\nnewlines'; for d in $(find . -type d -print); do echo "Found: $d"; done; }; rm -rf "$tempdir" )
und du wirst sehen, jedes Wort auf einer eigenen Zeile in der Ausgabe, mit seinen eigenenFound:
Präfix.akzeptierte Antwort brechen auf Leerzeichen, wenn die Verzeichnisnamen haben, und die bevorzugte syntax ist
$()
für die bash/ksh. VerwendenGNU find
-exec
option mit+;
zBfind .... -exec mycommand +;
#this is same as passing to xargs
oder verwenden Sie eine while-Schleife
find -exec
und Weitergabe anxargs
:find
ignoriert den exit-Wert der Befehl, der ausgeführt wird, währendxargs
scheitern wird auf einen Wert ungleich null beenden. Entweder könnte richtig sein, je nach Ihren Bedürfnissen.find ... -print0 | while IFS= read -r d
ist sicherer -- unterstützt Namen, die beginnen oder enden, Leerzeichen, Namen, die newline-Literale.