Frage Wie man jedes Verzeichnis im aktuellen Pfad eingibt und Skript ausführt


Ich möchte jedes Verzeichnis neu eingeben, das von ls Befehl und Skript ausführen.

Ich habe das (und viele andere Dinge) versucht, aber es funktioniert einfach nicht

ls  | awk '{print $1" && pwd"}' | xargs cd

Wie geht es ohne for-Schleife?


5
2017-11-09 14:29


Ursprung


Warum speziell? ls und keine For-Schleife? Die for-Schleife wäre der leichtere Ansatz, und außerdem wird sie bei Verzeichnissen, die Whitespace oder Anführungszeichen enthalten, nicht unterbrochen, so wie Sie Probleme beim Parsen bekommen ls Ausgabe. - geirha
Siehe auch: Wie gehe ich in jedes Verzeichnis und führe einen Befehl aus? bei Stapelüberlauf. - kenorb


Antworten:


Wenn du kannst, benutze find wie in anderen Antworten vorgeschlagen, xargs ist fast immer zu vermeiden.

Aber wenn du immer noch benutzen willst xargsEine mögliche Alternative ist die folgende:

printf '%s\0' */ | xargs -0 -L1 bash -c 'cd -- "$1" && pwd' _

Einige Notizen:

  1. */ wird durch den abschließenden Schrägstrich in die Liste der Verzeichnisse im aktuellen Ordner erweitert

  2. printf mit \0 (Nullbyte) trennt die Elemente für jede Zeile

  3. die Option -L1 zu xargs macht es für jede Eingabezeile und das Optionet einmal auszuführen -0 macht es die Eingabe auf das Null-Byte zu trennen: Dateinamen können ein beliebiges Zeichen enthalten, der Befehl bricht nicht!

  4. bash entfernt die Anführungszeichen und übergibt sie als einzigen Parameter an das Inline-Skript cd sollte wieder in Anführungszeichen gesetzt werden, um es als eine einzige Zeichenfolge zu interpretieren; verwenden -- macht das cd Befehl robust gegen Dateinamen, die mit einem Bindestrich beginnen

  5. um das zu vermeiden seltsam Gebrauch von $0 als ein Parameter ist es üblich, ein erstes Dummy-Argument zu setzen _


8
2017-11-09 15:05



ich wusste echo * aber war sich dessen nicht bewusst */. Und einen Unterstrich als erstes Argument zu setzen, ist ein netter Trick. - lgarzo
thx viel, ich lerne Bash und das ist sehr hilfreich. - UAdapter
@gniourf_gniourf: danke für die Bearbeitung und Bereitstellung eines robusteren Befehls. - enzotib


find . -type d -exec bash -c 'cd "$0" && pwd' {} \;

Wechsel pwd für dein Skript. Und . für den Stammverzeichnisnamen, wenn es nicht das "aktuelle" Verzeichnis ist.

Sie müssen die Exec-Klausel in umbrechen bash -c "..." wegen dem Weg -exec funktioniert. cd existiert nicht in seiner begrenzten Umgebung, aber wie Sie sehen können, indem Sie den obigen Befehl ausführen, wenn Sie bash zur Ausführung bringen, läuft alles wie vorhergesagt.

Bearbeiten: Ich bin mir nicht sicher, warum 2011 Oli nicht gedacht hat -execdir aber das ist wahrscheinlich eine schnellere Lösung:

find . -type d -execdir pwd \;

13
2017-11-09 15:04



Der Grund cd alleine funktioniert nicht, weil es eine eingebaute Shell ist, nichts mit der Umgebung zu tun hat. - enzotib
Nette Arbeit Oli. Ich fügte hinzu maxdepth hier wie zuvor erwähnt, auch da es scheint, dass aktuelle Unterverzeichnisse ich entfernt habe .: find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \; - Todd Partridge 'Gen2ly'


Der Befehl zum Suchen

Der Suchbefehl ist ziemlich praktisch. Es kann eine Liste von Dateien generieren oder in oder unter einem angegebenen Verzeichnis verwalten und es kann auch einen Befehl für jedes Element in dieser Ergebnisliste ausführen. (Hinweis: Wenn Sie ein Mehrbenutzersystem verwenden, lesen Sie den Abschnitt mit den Sicherheitshinweisen in der Findutils-Dokumentation für den Befehl "find".)

Die Optionen für den find-Befehl, die wir benötigen, sind -type d, die find anweist, die Namen jedes Verzeichnisses innerhalb des angegebenen Pfads zurückzugeben, und -exec-Befehl '{}' \; Das sagt find, um den Befehl auszuführen. Beispielsweise:

find /media/music/flac -type d -exec ~/tag-flac-with-rg.sh '{}' \;

Dies sagt find, jedes Verzeichnis in oder unter dem Pfad / media / music / flac zurückzugeben und dann das Skript tag-flac-with-rg.sh für jedes gefundene Verzeichnis auszuführen. Die '{}' wird durch jeden gefundenen Verzeichnisnamen ersetzt und als Argument an das Skript tag-flac-with-rg.sh übergeben. Das \; markiert das Ende der Argumentliste mit dem Skript tag-flac-with-rg.sh. Vielleicht wird dir das helfen


2
2017-11-09 15:05



Keine Notwendigkeit zu zitieren {}, und ein -maxdepth 1 Dies kann nützlich sein, wenn das OP nicht in Unterverzeichnisse zurückkehren möchte. - enzotib
@enzotib Ich wusste nicht über die -maxdepth 1, thx - UAdapter


Nur aus Neugier habe ich versucht, dein Beispiel zu nehmen und es zum Laufen zu bringen. Es gibt kein Grund, diese Technik zu verwenden anstatt der find Befehl.

Hinweis: Dank an Enzotib um darauf hinzuweisen, funktioniert dies nicht für Verzeichnisnamen, die Leerzeichen oder andere Zeichen enthalten   machen awkdenke, der Name besteht aus mehreren Feldern.

Um Ihr Beispiel zu arbeiten, sollten einige Optimierungen vorgenommen werden. Aber zuerst sehen Sie den Code:

ls -l | grep ^d | awk '{print $NF}' | xargs -n 1 bash -c 'cd $0; ls'

Die ersten beiden Befehle stellen sicher, dass nur Verzeichnisse aufgelistet sind. Schon seit ls lange Liste enthält a d als erstes Zeichen für Verzeichnisse, die grep Der Befehl wählt nur diese Zeilen aus.

Mit awk nur die letzte Spalte wird in jeder Zeile angezeigt. NF ist eine eingebaute Variable in awk Halten der maximalen Anzahl von Feldern. So Drucken $NF Zeigt das letzte Feld an.

Endlich xargs muss gezähmt werden, um das angegebene Skript mit jeweils nur einem Parameter ausführen zu können. (Ich weiß, dass diese Art von Zweck ihren Zweck besiegt.) Sonst könnte das Skript nicht für jedes Verzeichnis ausgeführt werden ohne eine for-Schleife.

Und um das eingebaute zu umgehen cd Barriere, wir rufen an bash mit -c. Beachten Sie auch das $0 wird der erste Parameter anstelle von $1.


2
2017-11-09 15:12



Wenn Sie Verzeichnisnamen haben, die Leerzeichen enthalten, $NF gibt nur das letzte Wort des Namens. Dies ist einer der Gründe, warum Menschen vorgeschlagen wird, nicht zu analysieren ls Ausgabe. - enzotib
Dies ist ein gültiger Punkt, und ich sehe keine einfache Problemumgehung, ohne ein langes zu erstellen awk Skript. Ich versuchte zu demonstrieren, wie das ursprüngliche Denken funktionieren könnte (und das OP davon zu überzeugen, es nicht zu benutzen, weil es zumindest nicht elegant ist). Danke, dass Sie darauf hingewiesen haben, ich werde die Antwort aktualisieren. - lgarzo
Sehen Sie meine Antwort, die mit arbeitet xargs. - enzotib
thx viel, ich lerne Bash und das ist sehr hilfreich. - UAdapter


Vielleicht werfen Sie einen Blick auf die Verwendung find mit -exec?


2
2017-11-09 14:34





Andere Alternative:

for directory in *
do
  (cd $directory && awk '{print $1" && pwd"}')
done

2
2018-03-20 14:56





Sie sollte nicht analysieren ls an erster Stelle.

Sie können GNU verwenden find dafür und seine -execdir Parameter, z.B.

find . -type d -execdir realpath "{}" ';'

oder mehr praktisches Beispiel:

find . -name .git -type d -execdir git pull -v ';'

und hier ist Version mit -exec:

find . -type d -print0 | xargs -0 -I% sh -c 'cd "%" && pwd && echo Do stuff'

Weitere Beispiele finden Sie unter: Wie gehe ich in jedes Verzeichnis und führe einen Befehl aus? bei SO


1
2017-10-21 20:17