2009-08-28 17:03:49 +0000 2009-08-28 17:03:49 +0000
101
101

Boucler &007 résulte en bash shell script

Quelqu'un a-t-il un modèle de script shell pour faire quelque chose avec ls pour une liste de noms de répertoires et boucler à travers chacun et faire quelque chose ?

Je prévois de faire ls pour obtenir la liste des noms de répertoires.

Réponses (7)

120
120
120
2009-08-28 17:09:37 +0000

*Vous pouvez remplacer le for..do..done par `ou tout autre glob qui renvoie une liste (de fichiers, de répertoires, ou de n'importe quoi d'ailleurs), une commande qui génère une liste, par exemple,*.txt`, ou en fait le remplacer par une liste.

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

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

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

Ou mieux encore, si vous utilisez Bash, vous pouvez utiliser les expansions de paramètres Bash plutôt que de créer une sous-coque :

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done
69
69
69
2012-04-29 22:16:24 +0000

Utiliser la sortie de ls pour obtenir des noms de fichiers est une mauvaise idée . Cela peut entraîner des dysfonctionnements et même des scripts dangereux. En effet, un nom de fichier peut contenir n'importe quel caractère, sauf / et le caractère null, et ls n'utilise aucun de ces caractères comme délimiteurs, donc si un nom de fichier comporte un espace ou un saut de ligne, vous obtiendrez des résultats inattendus.

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

La première consiste à utiliser les fonctions de globbing natives du shell. Le shell développe */ en arguments séparés que la boucle for lit ; même s'il y a un espace, un saut de 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 des sous-répertoires, alors cela ne fera pas l'affaire à moins que votre shell n'ait des fonctionnalités de globbing étendues (comme bash. Si votre shell n'a pas ces fonctionnalités, ou si vous voulez vous assurer que votre script fonctionnera sur une variété de systèmes, alors la prochaine option est d'utiliser globstar.

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

Ici, la commande find appellera find et lui passera un argument du nom du fichier. Elle le fait une fois pour chaque fichier qu'elle trouve. Comme dans l'exemple précédent, il n'y a pas d'analyse d'une liste de noms de fichiers ; à la place, un nom de fichier est envoyé complètement en argument.

La syntaxe de l'argument echo est un peu bizarre. -exec prend le premier argument après find et le traite comme le programme à exécuter, et chaque argument suivant, il le prend comme argument à passer à ce programme. Il y a deux arguments spéciaux que -exec doit voir. Le premier est -exec ; cet argument est remplacé par un nom de fichier que les parties précédentes de {} génèrent. Le second est find, ce qui permet à ; de savoir que c'est la fin de la liste des arguments à passer au programme ; find en a besoin car vous pouvez continuer avec d'autres arguments qui sont destinés à find et non destinés au programme exécuté. La raison du find est que le shell traite également le [ Utiliser la sortie delspour obtenir des noms de fichiers est une mauvaise idée ]&003. Cela peut entraîner des dysfonctionnements et même des scripts dangereux. En effet, un nom de fichier peut contenir n'importe quel caractère, sauf/et le caractèrenull, etls` n'utilise aucun de ces caractères comme délimiteurs, donc si un nom de fichier comporte un espace ou un saut de ligne, vous obtiendrez des résultats inattendus.

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

La première consiste à utiliser les fonctions de globbing natives du shell. Le shell développe */ en arguments séparés que la boucle for lit ; même s'il y a un espace, un saut de 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 des sous-répertoires, alors cela ne fera pas l'affaire à moins que votre shell n'ait des fonctionnalités de globbing étendues (comme bash. Si votre shell n'a pas ces fonctionnalités, ou si vous voulez vous assurer que votre script fonctionnera sur une variété de systèmes, alors la prochaine option est d'utiliser globstar.

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

Ici, la commande find appellera find et lui passera un argument du nom du fichier. Elle le fait une fois pour chaque fichier qu'elle trouve. Comme dans l'exemple précédent, il n'y a pas d'analyse d'une liste de noms de fichiers ; à la place, un nom de fichier est envoyé complètement en argument.

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

17
17
17
2009-08-28 17:26:27 +0000

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

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

ou, vous pouvez changer la variable d'environnement du séparateur de champs de saisie (IFS) :

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

Enfin, selon ce que vous faites, il se peut que vous n'ayez même pas besoin des ls :

for i in *; do echo "$i"; done;
5
5
5
2010-08-22 20:02:35 +0000

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

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

4
4
4
2015-02-11 14:36:01 +0000

Pour compléter la réponse de CoverosGene, voici un moyen d'énumérer uniquement les noms des répertoires :

for f in */; do
  echo "Directory -> $f"
done
1
1
1
2012-05-10 22:36:39 +0000

Pourquoi ne pas mettre IFS sur une nouvelle ligne, puis saisir la sortie de ls dans un tableau ? Le fait de mettre IFS sur une nouvelle ligne devrait résoudre les problèmes de caractères bizarres dans les noms de fichiers ; l'utilisation de ls peut être intéressante car elle dispose d'une fonction de tri intégrée.

(Lors des tests, j'ai eu du mal à mettre IFS sur \n mais le fait de le mettre sur une nouvelle ligne en arrière-plan fonctionne, comme suggéré ailleurs ici) :

Par exemple (en supposant que le modèle de recherche ls souhaité est 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.

-3
-3
-3
2009-08-28 17:28:24 +0000

C'est comme ça que je le fais, mais il y a probablement des moyens plus efficaces.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt