160 votes

En bouclant sur `ls`, on obtient bash Shell Shell.

Est-ce que quelqu'un a un modèle Shell Shell pour faire quelque chose avec ls pour une liste de noms de répertoires, puis de boucler sur chacun d'eux et de faire quelque chose ?

J'ai l'intention de faire ls -1d */ pour obtenir la liste des noms de répertoires.

190voto

pirho Points 1387

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

Il suffit d'utiliser un for..do..done boucle :

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

Vous pouvez remplacer le * avec *.txt ou tout autre glob qui renvoie une liste (de fichiers, de répertoires, ou de quoi que ce soit d'autre), une commande qui génère une liste, par ex, $(cat filelist.txt) ou le remplacer par une liste.

Dans le cadre de la do vous faites simplement référence à la variable de la 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 .xml dans le répertoire courant pour .txt :

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

Ou encore mieux, si vous utilisez Bash, vous pouvez utiliser les expansions de paramètres Bash plutôt que de lancer un sous-shell :

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

85voto

Joe Brinkman Points 834

En utilisant la sortie de ls pour obtenir les noms de fichiers est une mauvaise idée . Cela peut conduire à des dysfonctionnements, voire à des scripts dangereux. En effet, un nom de fichier peut contenir n'importe quel caractère sauf / et le null caractère, et ls n'utilise aucun de ces caractères comme délimiteurs. Par conséquent, si un nom de fichier comporte un espace ou un saut de ligne, vous pouvez utiliser la fonction se obtenir des résultats inattendus.

Il y a deux très bonnes façons d'itérer sur les fichiers. Ici, j'ai utilisé simplement echo pour démontrer qu'il faut faire quelque chose avec le nom du fichier ; vous pouvez cependant utiliser n'importe quoi.

La première consiste à utiliser les fonctionnalités natives de globbing du Shell.

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

Le Shell s'étend */ en arguments distincts que le for même s'il y a un espace, une nouvelle ligne ou tout autre caractère étrange dans le nom du fichier, for verra chaque nom complet comme une unité atomique ; il n'analyse pas la liste de quelque façon que ce soit.

Si vous voulez aller récursivement dans les sous-répertoires, alors cela ne fonctionnera pas à moins que votre Shell ait des fonctionnalités de globbing étendues (telles que bash 's globstar . Si votre Shell n'a pas ces fonctionnalités, ou si vous voulez vous assurer que votre Shell fonctionnera sur une variété de systèmes, alors l'option suivante est d'utiliser find .

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

Ici, le find appelle la commande echo et lui passe en argument le nom du fichier. Il le fait une fois pour chaque fichier qu'il trouve. Comme dans l'exemple précédent, il n'y a pas d'analyse syntaxique d'une liste de noms de fichiers ; au lieu de cela, un nom de fichier est envoyé complètement comme argument.

La syntaxe du -exec L'argument a l'air un peu bizarre. find prend le premier argument après -exec et le traite comme le programme à exécuter, et chaque argument suivant, il le prend comme un argument à passer à ce programme. Il y a deux arguments spéciaux que -exec a besoin de voir. Le premier est {} ; cet argument est remplacé par un nom de fichier que les parties précédentes de la commande find génère. Le second est ; qui permet find savoir que c'est la fin de la liste des arguments à passer au programme ; find a besoin de cela car on peut continuer avec plus d'arguments destinés find et non destiné au programme exécuté. La raison pour laquelle le \ est que le Shell traite également ; spécialement - il représente la fin d'une commande, donc nous devons l'échapper pour que le Shell le donne comme argument à find plutôt que de le consommer pour lui-même ; une autre façon d'obtenir que le Shell ne le traite pas spécialement est de le mettre entre guillemets : ';' fonctionne tout aussi bien que \; à cette fin.

39voto

Akselsson Points 702

Pour les fichiers contenant des espaces, vous devrez vous assurer de citer la variable, par exemple :

 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'aurez peut-être même pas besoin de l'option ls :

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

6voto

Ole Tange Points 4059

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 .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

4voto

n3o Points 141

Pour compléter la réponse de CoverosGene, voici une façon de lister uniquement les noms des répertoires :

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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X