Comment obtenir la liste des fichiers d'un répertoire dans un script shell?


159

J'essaye d'obtenir le contenu d'un répertoire en utilisant un script shell.

Mon script est:

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

$search_direst un chemin relatif. Cependant, $search_dircontient de nombreux fichiers avec des espaces dans leurs noms. Dans ce cas, ce script ne s'exécute pas comme prévu.

Je sais que je pourrais utiliser for entry in *, mais cela ne fonctionnerait que pour mon répertoire actuel.

Je sais que je peux accéder à ce répertoire, utiliser for entry in *puis revenir en arrière, mais ma situation particulière m'empêche de le faire.

J'ai deux chemins relatifs $search_diret $work_dir, et je dois travailler sur les deux simultanément, en les lisant, en créant / supprimant des fichiers, etc.

Alors qu'est-ce que je fais maintenant?

PS: j'utilise bash.

Réponses:


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

4
Pouvez-vous expliquer pourquoi for entry in "$search_dir/*"ne fonctionne pas? Pourquoi devons-nous placer en /*dehors des citations?
mrgloom

6
@mrgloom: Parce que vous devez laisser le shell ajouter le caractère générique.
Ignacio Vazquez-Abrams

ne serait pas findplus rapide?
Alexej Magura

4
La solution donne le chemin complet. Et si je voulais simplement lister ce qui se trouvait dans le répertoire actuel?
ThatsRightJack

1
@mrgloom si vous voulez le faire, vous pouvez y parvenir avecfor entry in "${search_dir}/*"
funilrys

28

Les autres réponses ici sont excellentes et répondent à votre question, mais c'est le meilleur résultat Google pour "bash obtenir la liste des fichiers dans le répertoire", (que je cherchais pour enregistrer une liste de fichiers) alors j'ai pensé publier un réponse à ce problème:

ls $search_path > filename.txt

Si vous voulez seulement un certain type (par exemple des fichiers .txt):

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

Notez que $ search_path est facultatif; ls> filename.txt fera le répertoire courant.


Pas besoin d'utiliser grep pour obtenir uniquement des fichiers .txt: `ls $ chemin_recherche / *. Txt> nomfichier.txt '. Mais plus important encore, il ne faut pas utiliser la sortie de la commande ls pour analyser les noms de fichiers.
Victor Zamanian

1
@VictorZamanian, pouvez-vous expliquer pourquoi nous ne devrions pas utiliser la sortie de lspour analyser les noms de fichiers? Je n'ai jamais entendu parler de cela auparavant.
samurai_jane

1
@samurai_jane Il y a beaucoup de liens à fournir sur ce sujet, mais voici un premier résultat de recherche: mywiki.wooledge.org/ParsingLs . J'ai même vu une question ici sur SO affirmant que les raisons de ne pas analyser la sortie de ls étaient BS et était très élaborée à ce sujet. Mais les réponses / réponses affirmaient toujours que c'était une mauvaise idée. Jetez un œil: unix.stackexchange.com/questions/128985/…
Victor Zamanian

26

C'est une façon de le faire où la syntaxe est plus simple à comprendre pour moi:

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

./est le répertoire de travail actuel mais peut être remplacé par n'importe quel chemin
*.txtrenvoie n'importe quoi.txt
Vous pouvez vérifier facilement ce qui sera répertorié en tapant la lscommande directement dans le terminal.

Fondamentalement, vous créez une variable yourfilenamescontenant tout ce que la commande de liste renvoie en tant qu'élément séparé, puis vous la parcourez en boucle. La boucle crée une variable temporaire eachfilequi contient un seul élément de la variable dans laquelle elle boucle, dans ce cas un nom de fichier. Ce n'est pas nécessairement mieux que les autres réponses, mais je trouve cela intuitif car je suis déjà familier avec la lscommande et la syntaxe de la boucle for.


Cela fonctionne bien pour un script rapide et informel ou une seule ligne, mais cela se cassera si un nom de fichier contient des nouvelles lignes, contrairement aux solutions basées sur glob.
Soren Bjornstad

@SorenBjornstad merci pour les conseils! Je ne savais pas que les nouvelles lignes étaient autorisées dans les noms de fichiers - quels types de fichiers pourraient en contenir? Comme, est-ce quelque chose qui se produit couramment?
rrr

Les nouvelles lignes dans les noms de fichiers sont mauvaises pour cette raison et pour autant que je sache, il n'y a aucune raison légitime de les utiliser. Je n'en ai jamais vu un dans la nature. Cela dit, il est tout à fait possible de construire de manière malveillante des noms de fichiers avec des nouvelles lignes de manière à exploiter cela. (Par exemple, imaginez un répertoire contenant des fichiers A, Bet C. Vous créez des fichiers appelés B\nCet D, puis choisissez de les supprimer. Un logiciel qui ne gère pas ce droit pourrait finir par supprimer les fichiers préexistants B et C à la place, même si vous ne disposiez pas de permission de le faire.)
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
Je sais que c'est assez vieux mais je n'arrive pas à obtenir la dernière xargs -0 -i echo "{}"commande, voulez-vous m'expliquer un peu? En particulier, que fait la -i echo "{}"partie? J'ai également lu la manpage qui -iest obsolète maintenant et nous devrions utiliser -Iinsted.
drkg4b

1
-iremplace {}par l'arg.
Noel Yap

Merci! C'est utile, aussi pour les lents d'esprit comme moi je pense que {}c'est la chaîne qui est remplacée par les correspondances par la findcommande.
drkg4b

3
pourquoi utilisez-vous xargs? par défaut, findimprime ce qu'il trouve ... vous pouvez tout supprimer -print0.
gniourf_gniourf

1
Faire cela ne gérerait pas bien les entrées de fichiers avec des espaces.
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

Variations

$ 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

Remarques:

  • . : dossier en cours
  • supprimer -maxdepth 1pour rechercher récursivement
  • -type f: trouver des fichiers, pas des répertoires ( d)
  • -not -path '*/\.*' : ne pas retourner .hidden_files
  • sed 's/^\.\///g': supprimer le préfixé ./de la liste de résultats

0

Voici une autre façon de lister les fichiers dans un répertoire (en utilisant un outil différent, pas aussi efficace que certaines des autres réponses).

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

echo *Sort tous les fichiers du répertoire courant. La forboucle itère sur chaque nom de fichier et s'imprime sur stdout.

De plus, si vous recherchez des répertoires dans le répertoire, placez-le dans la forboucle:

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

test -d vérifie si le fichier est un répertoire.


0

La réponse acceptée ne renverra pas le préfixe des fichiers avec un. Pour faire cela, utilisez

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

-1

Sur la version Linux avec laquelle je travaille (x86_64 GNU / Linux), les travaux suivants:

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

-1: C'est identique à la réponse acceptée sauf qu'elle ne cite pas les variables. Ne pas entre guillemets $search_dirsignifie qu'il se brise s'il y a un espace dans le nom du répertoire. (Dans ce cas, vous pouvez vous en tirer sans citer $entry, mais il serait préférable de le citer.)
Soren Bjornstad

Vous avez tort. Ma solution n'est pas la même, même si elle est similaire à la réponse acceptée. Et ce n'est PAS mal, cela fonctionne. Voici ce que j'ai essayé sur différents systèmes Unix, y compris Mac OS, et cela a fonctionné sur chacun d'entre eux. Le fichier avec des blancs, même s'il comporte des blancs en tête dans le nom, s'affiche correctement. search_dir =. pour l'entrée dans $ search_dir / *; faire echo $ entrée; done ./ aa aaa.txt ./add ./apm_tables.txt Vous ne pouvez voter contre les solutions que si vous pouvez prouver que la solution NE FONCTIONNE PAS ou produit des résultats erronés.
Andrushenko Alexander

Si vous lisez mon commentaire un peu plus près, vous verrez que le problème vient des espaces vides dans le nom du répertoire , pas dans le nom du fichier . C'est différent du problème rencontré par l'interrogateur, mais cela signifie toujours que le code peut se casser involontairement! Voici un exemple de ce qui ne fonctionne pas: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. La boucle s'exécute deux fois, une fois l'impression xet la deuxième fois y/*, ce qui n'est probablement pas le résultat attendu.
Soren Bjornstad

Et je considère que les variables non correctement citées sont dangereusement erronées, ce qui justifie un vote défavorable. Considérez ce code pour supprimer les sous - répertoires du répertoire de recherche tout en laissant les fichiers: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. S'il y a aussi un xrépertoire dans le répertoire courant, il est sommairement supprimé, simplement parce que vous n'avez pas cité votre variable!
Soren Bjornstad

Eh bien, vous appelez toujours un code créé pour une tâche, modifiez légèrement la tâche et le code se trompe. Pour la question posée, ma solution proposée fonctionne. Mais ... étant un développeur, je suis d'accord avec vos arguments: si nous pouvons rendre un code plus robuste et plus général, nous devrions le faire. Je vais donc ajouter les qoutes. Cela rendra ma réponse identique à la réponse donnée ci-dessus, mais pour des raisons de discussion (cela peut être utile pour quelqu'un), je laisse également la réponse.
Andrushenko Alexander

-1

Entrez simplement cette commande simple:

ls -d */

Pourriez-vous fournir quelques explications @Michael Sisko
Vipul

ls (liste) -d (liste uniquement les répertoires) * / (quel que soit le modèle, la barre oblique inverse limite la liste aux répertoires uniquement).
Michael Sisko
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.