Wie kann ich die Ergebnisse von find in einem Bash-Skript verarbeiten?

71

Ich versuche, ein Array zu verwenden, um eine Liste von Dateinamen mit dem findBefehl zu speichern .

Aus irgendeinem Grund funktioniert das Array in der von der Schule verwendeten Bash nicht, mein Programm funktioniert jedoch auf meinem eigenen Laptop.

Also habe ich mich gefragt, ob es einen anderen Weg gibt, das ist, was ich habe:

array = (`find . -name "*.txt"`)  #this will store all the .txt files into the array

Dann kann ich mit dem Befehl cat auf die Array-Elemente zugreifen und Kopien aller Dateien erstellen.

Gibt es eine andere Möglichkeit, dies ohne Verwendung eines Arrays zu tun?

Shellscriptbeginner
quelle
Wenn Sie nur an Dateien interessiert sind, sollten Sie die Ergebnisse von find mit einschränken -type f. Es ist gültig, ein Verzeichnis mit der Endung .txt zu haben, z. dir.txt
Uphill_ What '1

Antworten:

130

Sie könnten so etwas gebrauchen:

find . -name '*.txt' | while read line; do
    echo "Processing file '$line'"
done

ZB eine Kopie machen:

find . -name '*.txt' | while read line; do
    echo "Copying '$line' to /tmp"
    cp -- "$line" /tmp
done

HTH

Johannes Weiss
quelle
Die Verwendung einer forSchleife ist hier definitiv die bessere Wahl. Die Verwendung eines Arrays würde funktionieren, außer dass es die gesamte Liste in eine Variable liest und dann über die Variable iteriert. Diese Version liest jeden Dateinamen so wie er stammt findund verarbeitet ihn inline.
D.Shawley
D.Shawley, könnten Sie bitte ein Beispiel mit einer for-Schleife posten? Es sollte auch für Dateien mit Leerzeichen im Namen funktionieren, denke ich.
Johannes Weiss
2
Die Verwendung von find zusammen mit 'for' oder 'while' ist meistens eine schlechte Idee. Sie übergeben Dateinamen als Zeichenfolgen und erhalten alle Probleme mit Leerzeichen und Maskierung, während es kein Problem ist, das nicht maskierte {} zusammen mit find -exec, -execdir, -ok, -okdir zu verwenden. Versuchen Sie es a b.txt.
Benutzer unbekannt
2
Ich würde mit Konstrukt sehr vorsichtig sein, wenn Sie eine Schleife ausführen möchten, um Nebenwirkungen zu haben. Zum Beispiel a=1; seq 3 | while read line; do a=2; done; echo $adruckt1
Rvernica
1
Seien Sie vorsichtig: Dies wird whilein einer Subshell ausgeführt, da dies der zweite Befehl in der Pipeline ist. Daher sind Änderungen, die in dieser Schleife vorgenommen wurden, nach dem Beenden der Schleife nicht verfügbar. Ich hatte Probleme, dies an eine Variable anzuhängen, die außerhalb der Schleife deklariert wurde. Von hier: stackoverflow.com/questions/22691436/…
Illya Moskvin
25

Ich hatte ein Problem mit der Lösung von Johannes Weiß. Wenn ich nur ein Echo machen würde, würde es für die vollständige Liste der Dateien funktionieren. Wenn ich jedoch versuchen würde, ffmpeg in der nächsten Zeile auszuführen, würde das Skript nur die erste Datei verarbeiten, auf die es gestoßen ist. Ich nahm aufgrund der Pipe ein lustiges IFS-Geschäft an, konnte es aber nicht herausfinden und lief stattdessen mit einer for-Schleife:

for i in $(find . -name '*.mov' ); 
do
    echo "$i"
done
Sternpause
quelle
9
Dies schlägt für Leerzeichen in Dateinamen fehl
Spinup
12

Ich denke, Starpause hat die sauberste Lösung, schlägt jedoch fehl, wenn Leerzeichen in Pfaden vorhanden sind. Dies wird durch Einstellung behoben IFS. Die richtige Antwort lautet daher:

IFS=$'\n'
for i in $(find . -name '*.mov' ); 
do
    echo "$i"
done
unset IFS

Sie deaktivieren IFS, um das Verhalten für IFS zurückzusetzen, und warum dies $erforderlich ist IFS=$'\n', finden Sie unter /unix/184863/what-is-the-meaning-of-ifs-n-in- Bash-Scripting

berühmt
quelle
8

Setzen Sie nur keine Leerzeichen um das Gleichheitszeichen:

ar=($(find . -name "*.txt"))

Vermeiden Sie nach Möglichkeit Backticks, da diese veraltet sind. Sie können leicht mit Apostroph verwechselt werden, insbesondere in schlechten Schriftarten, und sie nisten nicht so gut.

In den meisten Fällen sind Sie am besten bedient, wenn Sie ein Suchergebnis direkt mit -exec, -execdir, -ok oder -okdir durchlaufen.

Für und während Schleifen sind schwer richtig zu machen, wenn es um Leerzeichen in Dateinamen oder Zeilenumbrüchen und Tabulatoren geht.

find ./ -name "*.txt" -exec grep {} ";"

Das {} muss nicht maskiert werden. Sie werden häufig eine Kombination aus find / xargs sehen, die auch einen zusätzlichen Prozess startet:

find ./ -name "*.txt" | xargs grep {} ";"
Benutzer unbekannt
quelle
3
find . -name '*.txt' | while IFS= read -r FILE; do
    echo "Copying $FILE.."
    cp "$FILE" /destination
done
Ghostdog74
quelle