Wie bekomme ich die Liste der Dateien in einem Verzeichnis in einem Shell-Skript?


157

Ich versuche, den Inhalt eines Verzeichnisses mithilfe eines Shell-Skripts abzurufen.

Mein Skript lautet:

for entry in `ls $search_dir`; do
    echo $entry
done

Wo $search_dirist ein relativer Pfad? Allerdings $search_direnthält viele Dateien mit Leerzeichen im Namen. In diesem Fall wird dieses Skript nicht wie erwartet ausgeführt.

Ich weiß, ich könnte verwenden for entry in *, aber das würde nur für mein aktuelles Verzeichnis funktionieren.

Ich weiß, dass ich in dieses Verzeichnis for entry in *wechseln, es dann wieder ändern kann, aber meine besondere Situation hindert mich daran.

Ich habe zwei relative Pfade $search_dirund $work_dir, und ich muss an beiden gleichzeitig arbeiten, sie lesen, Dateien darin erstellen / löschen usw.

Was mache ich jetzt?

PS: Ich benutze Bash.

Antworten:


270
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
Können Sie erklären, warum for entry in "$search_dir/*"nicht funktionieren? Warum müssen wir /*außerhalb von Anführungszeichen setzen?
Mrgloom

6
@mrgloom: Weil Sie die Shell den Platzhalter globalisieren lassen müssen.
Ignacio Vazquez-Abrams

wäre nicht findschneller?
Alexej Magura

4
Die Lösung gibt den vollen Weg. Was wäre, wenn ich nur auflisten wollte, was sich im aktuellen Verzeichnis befindet?
ThatsRightJack

1
@ Mrgloom, wenn Sie dies tun möchten, können Sie dies mitfor entry in "${search_dir}/*"
funilrys

28

Die anderen Antworten hier sind großartig und beantworten Ihre Frage, aber dies ist das beste Google-Ergebnis für "Bash Get List of Files in Directory" (nach dem ich gesucht habe, um eine Liste von Dateien zu speichern), also dachte ich, ich würde eine posten Antwort auf dieses Problem:

ls $search_path > filename.txt

Wenn Sie nur einen bestimmten Typ möchten (z. B. TXT-Dateien):

ls $search_path | grep *.txt > filename.txt

Beachten Sie, dass $ search_path optional ist. ls> filename.txt führt das aktuelle Verzeichnis aus.


Sie müssen grep nicht verwenden, um nur TXT-Dateien abzurufen: `ls $ search_path / *. Txt> filename.txt '. Noch wichtiger ist jedoch, dass die Ausgabe des Befehls ls nicht zum Parsen von Dateinamen verwendet werden sollte.
Victor Zamanian

1
@ VictorZamanian, können Sie erläutern, warum wir die Ausgabe von nicht lszum Parsen von Dateinamen verwenden sollten? Ich habe noch nie davon gehört.
Samurai_jane

1
@samurai_jane Zu diesem Thema gibt es viele Links, aber hier ist ein erstes Suchergebnis: mywiki.wooledge.org/ParsingLs . Ich habe hier sogar eine Frage zu SO gesehen, in der behauptet wurde, die Gründe dafür, dass die Ausgabe von ls nicht analysiert wurde, seien BS und sehr ausführlich. Aber die Antworten / Antworten behaupteten immer noch, es sei eine schlechte Idee. Schauen Sie mal rein
Victor Zamanian

26

Dies ist eine Möglichkeit, wenn die Syntax für mich einfacher zu verstehen ist:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./ist das aktuelle Arbeitsverzeichnis, kann aber durch einen Pfad ersetzt werden,
*.txtder irgendetwas zurückgibt. txt
Sie können überprüfen, was einfach aufgelistet wird, indem Sie den lsBefehl direkt in das Terminal eingeben .

Grundsätzlich erstellen Sie eine Variable, yourfilenamesdie alles enthält, was der Befehl list als separates Element zurückgibt, und durchlaufen sie dann. Die Schleife erstellt eine temporäre Variable eachfile, die ein einzelnes Element der Variablen enthält, die sie durchläuft, in diesem Fall einen Dateinamen. Dies ist nicht unbedingt besser als die anderen Antworten, aber ich finde es intuitiv, da ich bereits mit dem lsBefehl und der for-Schleifensyntax vertraut bin .


Dies funktioniert in Ordnung für ein schnelles, informelles Skript oder einen Einzeiler, wird jedoch unterbrochen, wenn ein Dateiname im Gegensatz zu den globbasierten Lösungen Zeilenumbrüche enthält.
Soren Bjornstad

@SorenBjornstad danke für den Rat! Ich wusste nicht, dass Zeilenumbrüche in Dateinamen zulässig sind - welche Art von Dateien könnten sie enthalten? Ist das etwas, das häufig vorkommt?
rrr

Zeilenumbrüche in Dateinamen sind aus diesem Grund böse und meines Wissens gibt es keinen legitimen Grund, sie zu verwenden. Ich habe selbst noch nie einen in freier Wildbahn gesehen. Es ist jedoch durchaus möglich, Dateinamen mit Zeilenumbrüchen in böswilliger Absicht so zu erstellen, dass dies ausgenutzt wird. (Zum Beispiel vorstellen , ein Verzeichnis Dateien enthält A, Bund C. Sie erstellen Dateien mit dem Namen B\nCund Dwählen Sie dann , sie zu löschen. Eine Software , die dieses Recht nicht umgehen konnte bereits existierende Dateien B und C am Ende Löschen statt , auch wenn Sie nicht haben , Erlaubnis dazu.)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
Ich weiß, dass dies ziemlich alt ist, aber ich kann anscheinend nicht den letzten xargs -0 -i echo "{}"Befehl bekommen. Möchtest du mich ein bisschen erklären? Was macht der -i echo "{}"Teil insbesondere? Außerdem habe ich von der manSeite gelesen, die -ijetzt veraltet ist und wir sollten -Iinsted verwenden.
drkg4b

1
-iersetzt {}mit dem arg.
Noel Yap

Vielen Dank! Dies ist nützlich, auch für Langsame wie mich. Ich denke, dass dies {}die Zeichenfolge ist, die durch den findBefehl durch die Übereinstimmungen ersetzt wird .
drkg4b

3
Warum verwenden Sie xargs? Standardmäßig wird findgedruckt, was gefunden wird. Sie können alles aus löschen -print0.
gniourf_gniourf

1
Andernfalls würden Dateieinträge mit Leerzeichen nicht gut verarbeitet.
Noel Yap

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Variationen

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Anmerkungen:

  • . : aktueller Ordner
  • entfernen -maxdepth 1, um rekursiv zu suchen
  • -type f: finde Dateien, keine Verzeichnisse ( d)
  • -not -path '*/\.*' : nicht zurückkehren .hidden_files
  • sed 's/^\.\///g': Entfernen Sie das vorangestellte ./aus der Ergebnisliste

0

Hier ist eine andere Möglichkeit, Dateien in einem Verzeichnis aufzulisten (mit einem anderen Tool, das nicht so effizient ist wie einige der anderen Antworten).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Gibt alle Dateien des aktuellen Verzeichnisses aus. Die forSchleife durchläuft jeden Dateinamen und wird in stdout gedruckt.

Wenn Sie im Verzeichnis nach Verzeichnissen suchen, platzieren Sie diese in der forSchleife:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d prüft, ob die Datei ein Verzeichnis ist.


0

Die akzeptierte Antwort gibt kein Dateipräfix mit einem zurück. Verwenden Sie dazu

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

In der Linux-Version, mit der ich arbeite (x86_64 GNU / Linux), funktionieren folgende Arbeiten:

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1: Dies ist identisch mit der akzeptierten Antwort, außer dass die Variablen nicht zitiert werden. $search_dirWenn Sie nicht zitieren , wird es unterbrochen, wenn der Verzeichnisname ein Leerzeichen enthält. (In diesem Fall können Sie davonkommen, ohne zu zitieren $entry, aber es wäre besser, es zu zitieren.)
Soren Bjornstad

Sie liegen falsch. Meine Lösung ist nicht dieselbe, auch wenn sie der akzeptierten Antwort ähnelt. Und es ist NICHT falsch, es funktioniert. Folgendes habe ich auf verschiedenen Unix-Systemen versucht, einschließlich Mac OS, und es hat auf jedem von ihnen funktioniert. Die Datei mit Leerzeichen wird ordnungsgemäß angezeigt, auch wenn der Name führende Leerzeichen enthält. search_dir =. für den Eintrag in $ search_dir / *; echo $ entry; erledigt ./ aa aaa.txt ./add ./apm_tables.txt Sie können Lösungen nur dann ablehnen, wenn Sie nachweisen können, dass die Lösung NICHT FUNKTIONIERT oder falsche Ergebnisse liefert.
Andrushenko Alexander

Wenn Sie meinen Kommentar etwas genauer lesen, werden Sie feststellen, dass das Problem mit Leerzeichen im Verzeichnisnamen und nicht im Dateinamen besteht . Das ist etwas anderes als das Problem, auf das der Fragesteller gestoßen ist, aber es bedeutet immer noch, dass der Code unbeabsichtigt brechen kann! Hier ist ein Beispiel dafür, was nicht funktioniert : mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Die Schleife wird zweimal ausgeführt, einmal gedruckt xund das zweite Mal y/*, was vermutlich nicht das erwartete Ergebnis ist.
Soren Bjornstad

Und ich halte falsch nicht zitierte Variablen für gefährlich falsch, was Grund für eine Ablehnung ist. Betrachten Sie diesen Code, um alle Unterverzeichnisse des Suchverzeichnisses zu löschen, während Sie die Dateien verlassen : search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Wenn sich xim aktuellen Verzeichnis auch ein Verzeichnis befindet, wird es zusammenfassend gelöscht, nur weil Sie Ihre Variable nicht angegeben haben!
Soren Björnstad

Nun, Sie nehmen immer einen Code, der für eine Aufgabe erstellt wurde, ändern die Aufgabe geringfügig und der Code wird falsch. Für die gestellte Frage funktioniert meine vorgeschlagene Lösung. Aber ... als Entwickler muss ich Ihren Argumenten zustimmen: Wenn wir einen Code robuster und allgemeiner machen können, sollten wir es tun. Also werde ich die Qouten hinzufügen. Dadurch wird meine Antwort mit der oben angegebenen Antwort identisch, aber aus Gründen der Diskussion (die für jemanden nützlich sein kann) lasse ich auch die Antwort.
Andrushenko Alexander

-1

Geben Sie einfach diesen einfachen Befehl ein:

ls -d */

Könnten Sie eine Erklärung geben @Michael Sisko
Vipul

ls (Liste) -d (nur Verzeichnisse auflisten) * / (bei jedem Muster beschränkt der Backslash die Liste nur auf Verzeichnisse).
Michael Sisko
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.