Quelqu'un a-t-il un modèle de script shell permettant de créer ls
une liste de noms de répertoires et de les parcourir en boucle?
Je prévois de faire ls -1d */
pour obtenir la liste des noms de répertoires.
Quelqu'un a-t-il un modèle de script shell permettant de créer ls
une liste de noms de répertoires et de les parcourir en boucle?
Je prévois de faire ls -1d */
pour obtenir la liste des noms de répertoires.
Réponses:
Ne pas utiliser ls à la place d'un glob, comme @ shawn-j-goff et d'autres l'ont suggéré.
Il suffit d'utiliser une for..do..done
boucle:
for f in *; do
echo "File -> $f"
done
Vous pouvez remplacer le *
with *.txt
ou tout autre glob qui retourne une liste (de fichiers, répertoires ou quoi que ce soit d'autre), une commande qui génère une liste, par exemple $(cat filelist.txt)
, ou bien la remplacer par une liste.
Dans la do
boucle, vous faites simplement référence à la variable de boucle avec le préfixe du signe dollar (comme $f
dans l'exemple ci-dessus). Vous pouvez echo
le faire ou faire tout ce que vous voulez.
Par exemple, pour renommer tous les .xml
fichiers du répertoire en cours 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ètres 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
for
boucles et le piège typique d’essayer d’analyser la sortie de ls
. @ DanielA.White, vous pouvez envisager d’accepter cette réponse si elle n’est pas utile (ou si elle est potentiellement trompeuse), car, comme vous l’avez dit, vous agissez sur des annuaires. La réponse de Shawn J. Goff devrait fournir une solution plus robuste et plus efficace à votre problème.
ls
sortie , vous ne devriez pas lire la sortie dans une for
boucle , vous devriez utiliser à la $()
place de `` et Use More Quotes ™ . Je suis sûr que @CoverosGene était bien intentionné, mais c’est terrible.
ls
est ls -1 | while read line; do stuff; done
. Au moins celui-ci ne cassera pas pour les espaces blancs.
mv -v
vous n'avez pas besoinecho "moved $x -> $t"
Utiliser la sortie de ls
pour obtenir les noms de fichiers est une mauvaise idée . Cela peut conduire à des scripts défectueux et même dangereux. En effet , un nom de fichier peut contenir des caractères à l' exception /
et le null
caractère, et ls
ne pas utiliser l' un de ces personnages comme délimiteurs, donc si un nom de fichier a un espace ou un saut de ligne, vous sera obtenir des résultats inattendus.
Il existe deux très bons moyens de parcourir les fichiers. Ici, j’ai simplement utilisé echo
pour montrer comment faire quelque chose avec le nom de fichier; vous pouvez utiliser n'importe quoi, cependant.
La première consiste à utiliser les fonctions de globbing natives du shell.
for dir in */; do
echo "$dir"
done
Le shell se développe */
en arguments séparés lus par la for
boucle; même s'il y a un espace, une nouvelle ligne ou tout autre caractère étrange dans le nom du fichier, for
chaque nom complet sera vu comme une unité atomique; ce n'est pas analyser la liste en aucune façon.
Si vous voulez aller récursivement dans les sous-répertoires, cela ne fonctionnera que si votre shell dispose de fonctionnalités étendues (telles que celles bash
de globstar
.). 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, l'option suivante consiste à utiliser find
.
find . -type d -exec echo '{}' \;
Ici, la find
commande appellera echo
et lui passera un argument du nom de fichier. Il le fait une fois pour chaque fichier trouvé. Comme dans l'exemple précédent, il n'y a pas d'analyse d'une liste de noms de fichiers; au lieu de cela, un nom de fichier est envoyé complètement comme argument.
La syntaxe de l' -exec
argument semble un peu drôle. find
prend le premier argument après -exec
et traite cela comme le programme à exécuter, et chaque argument suivant, il le prend comme argument à transmettre à ce programme. Il y a deux arguments spéciaux -exec
à voir. Le premier est {}
; cet argument est remplacé par un nom de fichier find
généré par les parties précédentes . La seconde est ;
, ce qui permet de find
savoir que ceci est la fin de la liste des arguments à transmettre au programme; find
a besoin de cela car vous pouvez continuer avec plus d’arguments find
destinés au programme exec'd et non au programme exec'd. La raison en \
est que la coquille traite également;
spécialement - il représente la fin d'une commande, nous avons donc besoin de l'échapper pour que le shell l'envoie comme argument find
plutôt que de le consommer pour lui-même; Une autre façon de faire en sorte que la coquille ne la traite pas spécialement est de la mettre entre guillemets: cela ';'
fonctionne aussi bien que \;
pour le faire.
find
. Il est possible de faire de la magie et même de -exec
contourner les limites perçues . Cette -print0
option est également utile à utiliser avec xargs
.
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;
\n
est insuffisante. Ce n'est jamais une bonne idée de recommander l'analyse de la sortie de ls
.
Si vous avez installé GNU Parallel http://www.gnu.org/software/parallel/, 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
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 agréable car il dispose d’une fonction de tri intégrée.
(Lors des tests, j'avais du mal à définir IFS sur, \n
mais le réglage sur newline backspace fonctionne, comme suggéré ailleurs ici):
Par exemple (en supposant que le ls
modèle de recherche souhaité a été transmis $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 ls
commande est ls -t -U -r
.
\n
est insuffisante. Les seules solutions valides utilisent une boucle for avec extension de nom de chemin, ou find
.
Voici comment je le fais, mais il existe probablement des moyens plus efficaces.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done