2009-08-28 17:03:49 +0000 2009-08-28 17:03:49 +0000
101
101

Das Durchschleifen von &007 führt zu einem Bash-Shell-Skript

Hat jemand ein Vorlage-Shell-Skript, um etwas mit ls für eine Liste von Verzeichnisnamen zu tun und jedes einzelne durchzuschleifen und etwas zu tun?

Ich habe vor, ls zu tun, um die Liste der Verzeichnisnamen zu erhalten.

Antworten (7)

120
120
120
2009-08-28 17:09:37 +0000

Editiert, um ls nicht dort zu verwenden, wo es ein glob tun würde, wie @shawn-j-goff und andere vorschlugen.

Benutzen Sie einfach eine for..do..done-Schleife:

for f in *; do
  echo "File -> $f"
done

Sie können die * durch *.txt oder jedes andere glob ersetzen, das eine Liste (von Dateien, Verzeichnissen oder irgendetwas in dieser Hinsicht) zurückgibt, ein Befehl, der eine Liste erzeugt, z.B,

Innerhalb der $(cat filelist.txt)-Schleife beziehen Sie sich einfach auf die Schleifenvariable mit dem Dollarzeichen-Präfix (also do im obigen Beispiel). Sie können sie $f oder alles andere mit ihr machen, was Sie wollen.

Zum Beispiel, um alle echo-Dateien im aktuellen Verzeichnis in .xml umzubenennen:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

Oder noch besser, wenn Sie die Bash verwenden, können Sie Bash-Parameter-Erweiterungen verwenden, anstatt eine Subshell zu erzeugen:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
69
69
69
2012-04-29 22:16:24 +0000

Die Ausgabe von ls zu verwenden, um Dateinamen zu erhalten, ist eine schlechte Idee . Es kann zu Fehlfunktionen und sogar zu gefährlichen Skripten führen. Das liegt daran, dass ein Dateiname jedes beliebige Zeichen außer / und dem Zeichen null enthalten kann, und ls verwendet keines dieser Zeichen als Trennzeichen. Wenn ein Dateiname also ein Leerzeichen oder einen Zeilenumbruch enthält, werden Sie unerwartete Ergebnisse erhalten.

Es gibt zwei sehr gute Möglichkeiten, über Dateien zu iterieren. Hier habe ich einfach echo verwendet, um zu demonstrieren, wie man etwas mit dem Dateinamen macht; man kann aber alles verwenden.

Die erste ist, die nativen Globbing-Funktionen der Shell zu verwenden.

for dir in */; do
  echo "$dir"
done

Die Shell erweitert */ in separate Argumente, die die for-Schleife liest; selbst wenn der Dateiname ein Leerzeichen, einen Zeilenumbruch oder ein anderes seltsames Zeichen enthält, sieht for jeden vollständigen Namen als atomare Einheit; die Liste wird in keiner Weise geparst.

Wenn Sie rekursiv in Unterverzeichnisse gehen wollen, dann geht das nicht, es sei denn, Ihre Shell hat einige erweiterte Globbing-Funktionen (wie bash‘s globstar. Wenn Ihre Shell nicht über diese Funktionen verfügt oder wenn Sie sicherstellen wollen, dass Ihr Skript auf einer Vielzahl von Systemen funktioniert, dann ist die nächste Option die Verwendung von find.

find . -type d -exec echo '{}' \;

Hier wird der Befehl find echo aufrufen und ihm ein Argument des Dateinamens übergeben. Er tut dies einmal für jede Datei, die er findet. Wie im vorigen Beispiel gibt es kein Parsen einer Liste von Dateinamen; stattdessen wird ein Dateiname vollständig als Argument gesendet.

Die Syntax des Arguments -exec sieht ein wenig komisch aus. find nimmt das erste Argument nach -exec und behandelt es als das auszuführende Programm, und jedes weitere Argument wird als Argument an dieses Programm übergeben. Es gibt zwei spezielle Argumente, die -exec sehen muss. Das erste ist {}; dieses Argument wird durch einen Dateinamen ersetzt, den die vorherigen Teile von find erzeugen. Das zweite ist ;, das find wissen lässt, dass dies das Ende der Liste der an das Programm zu übergebenden Argumente ist; find braucht dies, weil Sie mit weiteren Argumenten fortfahren können, die für find und nicht für das ausgeführte Programm bestimmt sind. Der Grund für [ Die Ausgabe vonlszu verwenden, um Dateinamen zu erhalten, ist eine schlechte Idee ]&003. Es kann zu Fehlfunktionen und sogar zu gefährlichen Skripten führen. Das liegt daran, dass ein Dateiname jedes beliebige Zeichen außer/und dem Zeichennullenthalten kann, undls` verwendet keines dieser Zeichen als Trennzeichen. Wenn ein Dateiname also ein Leerzeichen oder einen Zeilenumbruch enthält, werden Sie unerwartete Ergebnisse erhalten.

Es gibt zwei sehr gute Möglichkeiten, über Dateien zu iterieren. Hier habe ich einfach echo verwendet, um zu demonstrieren, wie man etwas mit dem Dateinamen macht; man kann aber alles verwenden.

Die erste ist, die nativen Globbing-Funktionen der Shell zu verwenden.

for dir in */; do
  echo "$dir"
done

Die Shell erweitert */ in separate Argumente, die die for-Schleife liest; selbst wenn der Dateiname ein Leerzeichen, einen Zeilenumbruch oder ein anderes seltsames Zeichen enthält, sieht for jeden vollständigen Namen als atomare Einheit; die Liste wird in keiner Weise geparst.

Wenn Sie rekursiv in Unterverzeichnisse gehen wollen, dann geht das nicht, es sei denn, Ihre Shell hat einige erweiterte Globbing-Funktionen (wie bash’s globstar. Wenn Ihre Shell nicht über diese Funktionen verfügt oder wenn Sie sicherstellen wollen, dass Ihr Skript auf einer Vielzahl von Systemen funktioniert, dann ist die nächste Option die Verwendung von find.

find . -type d -exec echo '{}' \;

Hier wird der Befehl find echo aufrufen und ihm ein Argument des Dateinamens übergeben. Er tut dies einmal für jede Datei, die er findet. Wie im vorigen Beispiel gibt es kein Parsen einer Liste von Dateinamen; stattdessen wird ein Dateiname vollständig als Argument gesendet.

Die Syntax des Arguments -exec sieht ein wenig komisch aus. find nimmt das erste Argument nach -exec und behandelt es als das auszuführende Programm, und jedes weitere Argument wird als Argument an dieses Programm übergeben. Es gibt zwei spezielle Argumente, die -exec sehen muss. Das erste ist {}; dieses Argument wird durch einen Dateinamen ersetzt, den die vorherigen Teile von find erzeugen. Das zweite ist ;, das find wissen lässt, dass dies das Ende der Liste der an das Programm zu übergebenden Argumente ist; find braucht dies, weil Sie mit weiteren Argumenten fortfahren können, die für find und nicht für das ausgeführte Programm bestimmt sind. Der Grund für ist, dass die Shell auch ; speziell behandelt - es stellt das Ende eines Befehls dar, also müssen wir ihm entkommen, damit die Shell es als Argument an find übergibt, anstatt es für sich selbst zu verbrauchen; eine andere Möglichkeit, die Shell dazu zu bringen, es nicht speziell zu behandeln, besteht darin, es in Anführungszeichen zu setzen: ';' funktioniert für diesen Zweck genauso gut wie \;.

17
17
17
2009-08-28 17:26:27 +0000

Bei Dateien mit Leerzeichen müssen Sie darauf achten, die Variable wie folgt in Anführungszeichen zu setzen:

for i in $(ls); do echo "$i"; done;

oder Sie können die IFS-Umgebungsvariable (Input Field Separator) ändern:

IFS=$'\n';for file in $(ls); do echo $i; done

Schließlich, je nachdem, was Sie tun, benötigen Sie vielleicht nicht einmal die ls:

for i in *; do echo "$i"; done;
5
5
5
2010-08-22 20:02:35 +0000

Wenn Sie GNU Parallel http://www.gnu.org/software/parallel/ installiert haben, können Sie dies tun:

ls | parallel echo {} is in this dir

Um alle .txt in .xml umzubenennen:

ls *.txt | parallel mv {} {.}.xml

Schauen Sie sich das Einführungsvideo für GNU Parallel an, um mehr zu erfahren http://www.youtube.com/watch?v=OpaiGYxkSuQ

4
4
4
2015-02-11 14:36:01 +0000

Um die Antwort von CoverosGene zu ergänzen, hier ist eine Möglichkeit, nur die Verzeichnisnamen aufzulisten:

for f in */; do
  echo "Directory -> $f"
done
1
1
1
2012-05-10 22:36:39 +0000

Warum setzen Sie IFS nicht auf einen Zeilenumbruch und erfassen dann die Ausgabe von ls in einem Array? Das Setzen von IFS auf Zeilenvorschub sollte Probleme mit komischen Zeichen in Dateinamen lösen; die Verwendung von ls kann nett sein, weil es eine eingebaute Sortierfunktion hat.

(Beim Testen hatte ich Probleme, IFS auf \n zu setzen, aber das Setzen auf Zeilenvorschub funktioniert, wie an anderer Stelle hier vorgeschlagen wird):

Z.B. (unter der Annahme, dass das gewünschte Suchmuster ls in $1 übergeben wird):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

Dies ist besonders praktisch unter OS X, z.B, um eine nach Erstellungsdatum (älteste bis neueste) sortierte Liste von Dateien zu erfassen, lautet der Befehl ls ls -t -U -r.

-3
-3
-3
2009-08-28 17:28:24 +0000

So mache ich es, aber es gibt wahrscheinlich effizientere Wege.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt