Das Durchlaufen von 'ls' führt zu einem Bash-Shell-Skript

269335
Daniel A. White

Hat jemand ein Template-Shell-Skript, um etwas mit lseiner Liste von Verzeichnisnamen zu tun und jedes durchzugehen und etwas zu tun?

Ich habe vor ls -1d */, die Liste der Verzeichnisnamen abzurufen.

75

7 Antworten auf die Frage

78
CoverosGene

Bearbeitet, um ls nicht dort zu verwenden, wo ein Glob dies tun würde, wie @ shawn-j-goff und andere vorschlugen.

Verwenden Sie einfach eine for..do..doneSchleife:

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

Sie können die ersetzen *mit *.txtoder anderen glob, die eine Liste (von Dateien, Verzeichnissen oder etwas für diese Angelegenheit) zurückkehren, einen Befehl, der eine Liste erzeugt, zum Beispiel $(cat filelist.txt), oder es tatsächlich ersetzt mit einer Liste.

Innerhalb der doSchleife verweisen Sie nur auf die Schleifenvariable mit dem Dollarzeichen-Präfix ( $fim obigen Beispiel). Sie können echoes oder etwas anderes tun, was Sie wollen.

Um beispielsweise alle .xmlDateien im aktuellen Verzeichnis umzubenennen in .txt:

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 Bash verwenden, können Sie Bash-Parametererweiterungen verwenden, anstatt eine Subshell aufzurufen:

for x in *.xml; do  t=$.txt mv $x $t && echo "moved $x -> $t" done 
Was ist, wenn der Dateiname ein Leerzeichen enthält? Daniel A. White vor 14 Jahren 19
Ich iteriere alle Ordner. Daniel A. White vor 14 Jahren 0
Leider, wie Daniel gesagt hat, wird der Code in dieser Antwort beschädigt, wenn eine der Dateien oder Ordner ein Leerzeichen oder einen Zeilenumbruch in ihrem Namen enthält. Es zeigt einen sehr häufigen Missbrauch von "for" - Schleifen und eine typische Fallstricke beim Versuch, die Ausgabe von "ls" zu analysieren. @ DanielA.White, Sie denken vielleicht, diese Antwort nicht anzunehmen, wenn sie nicht hilfreich wäre (oder möglicherweise irreführend ist), da Sie, wie Sie sagten, auf Verzeichnisse reagieren. Shawn J. Goffs Antwort sollte eine robustere und funktionierende Lösung für Ihr Problem bieten. slhck vor 11 Jahren 4
-∞ [Sie sollten die Ausgabe von "ls" nicht analysieren] (http://mywiki.wooledge.org/ParsingLs), [Sie sollten die Ausgabe nicht in einer for-Schleife lesen] (http: //mywiki.wooledge.). org / BashFAQ / 001), sollten Sie `$ ()` anstelle von `` und [Use More Quotes ™] (http://mywiki.wooledge.org/Quotes) verwenden. Ich bin mir sicher, dass @CoverosGene gut gemeint ist, aber das ist einfach schrecklich. l0b0 vor 10 Jahren 4
Eine bessere Alternative, wenn Sie `ls` wirklich verwenden möchten, ist` ls -1 | während Zeile lesen; Sachen machen; fertig ". Zumindest wird dieser nicht für Whitespaces brechen. Emmanuel Joubaud vor 9 Jahren 1
mit 'mv -v' brauchen Sie nicht 'echo "bewegt $ x -> $ t" ` DmitrySandalov vor 8 Jahren 1
63
Shawn J. Goff

Die Ausgabe von lszu verwenden, um Dateinamen abzurufen, ist eine schlechte Idee . Dies kann zu Fehlfunktionen und sogar zu gefährlichen Skripts führen. Dies liegt daran, dass ein Dateiname alle Zeichen außer enthalten kann /und das nullZeichen, und lsnicht eine dieser beiden Zeichen als Trennzeichen verwenden, so dass, wenn ein Dateiname ein Leerzeichen oder eine neue Zeile hat, Sie werden unerwartete Ergebnisse erhalten.

Es gibt zwei sehr gute Möglichkeiten, Dateien zu durchlaufen. Hier habe ich einfach echogezeigt, wie ich etwas mit dem Dateinamen machen kann. Sie können jedoch alles verwenden.

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

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

Die Shell erweitert sich */in separate Argumente, die die forSchleife liest. Auch wenn der Dateiname ein Leerzeichen, eine neue Zeile oder ein anderes merkwürdiges Zeichen enthält, forwird jeder vollständige Name als atomare Einheit angezeigt . Die Liste wird in keiner Weise analysiert.

Wenn Sie rekursiv in Unterverzeichnissen gehen wollen, dann wird dies nicht tun, wenn Ihre Shell einige erweiterte Globbing Funktionen (zB bash‚s globstar. Wenn Ihre Shell nicht über diese Eigenschaften haben, oder wenn Sie wollen sicherstellen, dass Ihr Skript funktioniert Bei einer Vielzahl von Systemen ist die nächste Option die Verwendung find.

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

Hier findruft der Befehl echoein Argument des Dateinamens auf und übergibt ihm. Es macht dies einmal für jede gefundene Datei. Wie im vorherigen Beispiel gibt es keine Analyse einer Liste von Dateinamen. Stattdessen wird ein Fileneame vollständig als Argument gesendet.

Die Syntax des -execArguments sieht etwas komisch aus. findnimmt das erste Argument nach -execund behandelt es als auszuführendes Programm, und jedes nachfolgende Argument nimmt es als Argument an das Programm. Es gibt zwei besondere Argumente, -execdie zu sehen sind. Der erste ist {}; Dieses Argument wird durch einen Dateinamen ersetzt, den die vorherigen Teile von findgenerieren. Die zweite ist ;, die findwissen lässt, dass dies das Ende der Liste der Argumente ist, die an das Programm übergeben werden sollen; findbenötigt dies, da Sie mit weiteren Argumenten fortfahren können, finddie für das ausgeführte Programm bestimmt sind und nicht für dieses Programm bestimmt sind. Der Grund für das \ist, dass die Schale auch behandelt;speziell - es stellt das Ende eines Befehls dar, also müssen wir es entziehen, so dass es von der Shell als Argument ausgegeben wird, findanstatt es für sich selbst zu verbrauchen; Eine andere Möglichkeit, die Shell dazu zu bringen, sie nicht speziell zu behandeln, besteht darin, sie in Anführungszeichen zu setzen: ';'funktioniert genauso gut wie \;für diesen Zweck.

+1 Dies ist definitiv der Weg, wenn Sie eine Dateiliste erstellen und in einem Befehl verwenden müssen. find -exec ist darauf beschränkt, nur einzelne Befehle ausführen zu können. Mit der Schleife können Sie nach Herzenslust pfeifen. MaQleod vor 11 Jahren 2
+1. Ich wünschte, mehr Leute würden 'find' verwenden. Es gibt Magie, die getan werden kann, und sogar die wahrgenommenen Einschränkungen von "-exec" können umgangen werden. Die Option "-print0" ist auch für die Verwendung mit "xargs" von Nutzen. ghoti vor 9 Jahren 0
7
john

Für Dateien mit Leerzeichen müssen Sie die Variable wie folgt zitieren:

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

oder Sie können die Umgebungsvariable Eingabefeldtrennzeichen (IFS) ändern:

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

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

 for i in *; do echo "$i"; done; 
nette Verwendung einer Subshell im ersten Beispiel Jeremy L vor 14 Jahren 0
Warum benötigt IFS nach der Zuweisung und vor dem neuen Zeichen ein $? Andy Ibanez vor 11 Jahren 0
Dateinamen können auch Zeilenumbrüche enthalten. Das Aufbrechen von \ n ist unzureichend. Es empfiehlt sich nie, die Ausgabe von 'ls' zu analysieren. ghoti vor 9 Jahren 3
5
Ole Tange

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

ls | parallel echo {} is in this dir 

Um alle .txt in .xml umzubenennen:

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

In dem Intro-Video von GNU Parallel erfahren Sie mehr: http://www.youtube.com/watch?v=OpaiGYxkSuQ

4
n3o

Um nur die Antwort von CoverosGene hinzuzufügen, können Sie nur die Verzeichnisnamen auflisten:

for f in */; do echo "Directory -> $f" done 
1
jetset

Warum setzen Sie IFS nicht auf eine neue Zeile und erfassen dann die Ausgabe lsin einem Array? Durch das Festlegen von Zeilenumbrüchen für IFS sollten Probleme mit lustigen Zeichen in Dateinamen behoben werden. Die Verwendung lskann schön sein, da sie über eine integrierte Sortierfunktion verfügt.

(Beim Testen hatte ich Probleme mit der Einstellung von IFS auf, \naber die Einstellung von Zeilenumbrüchen funktioniert, wie an anderer Stelle vorgeschlagen):

ZB (unter der Annahme, dass das gewünschte lsSuchmuster übergeben wurde $1):

SAVEIFS=$IFS IFS=$(echo -en "\n\b")  FILES=($(/bin/ls "$1"))  for AFILE in $ do ... do something with a file ... done  IFS=$SAVEIFS 

Dies ist besonders praktisch unter OS X, z. B. zum Erfassen einer Liste von Dateien, sortiert nach dem Erstellungsdatum (älteste bis neueste), lslautet der Befehl ls -t -U -r.

Dateinamen können auch Zeilenumbrüche enthalten. Dies ist häufig der Fall, wenn Benutzer ihre eigenen Dateien benennen dürfen. Das Aufbrechen von \ n ist unzureichend. Die einzigen gültigen Lösungen verwenden eine for-Schleife mit Pfadnamenerweiterung oder "find". ghoti vor 9 Jahren 2
Die einzige zuverlässige Methode zum Übertragen einer Liste von Dateinamen besteht darin, sie mit einem NUL-Zeichen zu trennen, da dies der einzige ist, der definitiv nicht in einem Dateipfad enthalten ist. glglgl vor 9 Jahren 0
-3
TREE

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

ls > filelist.txt  while read filename; do echo filename: "$filename"; done < filelist.txt 
Pfeifen statt der Datei anhalten:> `ls | während ich lese; do echo Dateiname: $ i; fertig " Jeremy L vor 14 Jahren 6
Cool. Ich sollte sagen, dass Sie auch $ EDITOR filelist.txt zwischen den beiden Befehlen verwenden können. Vieles, was Sie in einem Editor tun können, ist einfacher als in der Befehlszeile. Nicht relevant für diese Frage. TREE vor 14 Jahren 0
Ihre Lösung spricht das Problem mit Dateinamen, die Zeilenumbrüche enthalten, nicht an. glglgl vor 9 Jahren 0