web-dev-qa-db-fra.com

boucle dans les résultats `ls` dans le script bash shell

Quelqu'un a-t-il un modèle de script Shell pour faire quelque chose avec ls pour obtenir une liste de noms de répertoires et les parcourir en boucle pour ensuite faire quelque chose?

Je prévois de faire ls -1d */ pour obtenir la liste des noms de répertoire.

87
Daniel A. White

Édité pour ne pas utiliser ls là où un glob ferait, comme @ shawn-j-goff et d'autres ont suggéré.

Utilisez simplement une boucle for..do..done:

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

Vous pouvez remplacer le * par le *.txt ou tout autre objet renvoyant une liste (de fichiers, de répertoires ou quoi que ce soit d'autre), une commande générant une liste, par exemple, $(cat filelist.txt), ou le remplacer par une liste.

Dans la boucle do, vous faites simplement référence à la variable de boucle avec le préfixe du signe dollar (donc $f dans l'exemple ci-dessus). Vous pouvez echo ou faire tout ce que vous voulez.

Par exemple, pour renommer tous les fichiers .xml du répertoire actuel en .txt:

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

Ou mieux encore, si vous utilisez Bash, vous pouvez utiliser les extensions de paramètre Bash plutôt que de générer un sous-shell:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
100
CoverosGene

Pour les fichiers contenant des espaces, vous devrez vous assurer de citer la variable comme suit:

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

ou, vous pouvez modifier la variable d'environnement IFS (Input Field Separator):

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

Enfin, selon ce que vous faites, vous n’avez peut-être même pas besoin du ls:

 for i in *; do echo "$i"; done;
14
john

Si vous avez GNU Parallel http://www.gnu.org/software/parallel/ installé, vous pouvez le faire:

ls | parallel echo {} is in this dir

Pour renommer tous les fichiers .txt en .xml:

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

Regardez la vidéo d'introduction de GNU Parallel pour en savoir plus: http://www.youtube.com/watch?v=OpaiGYxkSuQ

5
Ole Tange

Pour ajouter à la réponse de CoverosGene, voici un moyen de ne lister que les noms de répertoires:

for f in */; do
  echo "Directory -> $f"
done
4
n3o

Pourquoi ne pas définir IFS sur une nouvelle ligne, puis capturer la sortie de ls dans un tableau? Définir IFS sur newline devrait résoudre les problèmes de caractères amusants dans les noms de fichiers; utiliser ls peut être Nice car il dispose d’une fonction de tri intégrée.

(Lors des tests, je rencontrais des difficultés pour définir IFS sur \n, mais le réglage sur newline backspace fonctionne, comme suggéré ailleurs ici):

Par exemple. (en supposant que le modèle de recherche ls souhaité soit passé dans $1):

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

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

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

IFS=$SAVEIFS

Ceci est particulièrement pratique sous OS X, par exemple pour capturer une liste de fichiers triés par date de création (du plus ancien au plus récent), la commande ls est ls -t -U -r.

1
jetset