Quelqu'un a-t-il un modèle de script shell permettant de créer lsune 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 lsune 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..doneboucle:
for f in *; do
echo "File -> $f"
done
Vous pouvez remplacer le *with *.txtou 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 doboucle, vous faites simplement référence à la variable de boucle avec le préfixe du signe dollar (comme $fdans l'exemple ci-dessus). Vous pouvez echole faire ou faire tout ce que vous voulez.
Par exemple, pour renommer tous les .xmlfichiers 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
forboucles 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.
lssortie , vous ne devriez pas lire la sortie dans une forboucle , vous devriez utiliser à la $()place de `` et Use More Quotes ™ . Je suis sûr que @CoverosGene était bien intentionné, mais c’est terrible.
lsest ls -1 | while read line; do stuff; done. Au moins celui-ci ne cassera pas pour les espaces blancs.
mv -vvous n'avez pas besoinecho "moved $x -> $t"
Utiliser la sortie de lspour 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 nullcaractère, et lsne 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é echopour 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 forboucle; même s'il y a un espace, une nouvelle ligne ou tout autre caractère étrange dans le nom du fichier, forchaque 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 bashde 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 findcommande appellera echoet 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' -execargument semble un peu drôle. findprend le premier argument après -execet 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 findgénéré par les parties précédentes . La seconde est ;, ce qui permet de findsavoir que ceci est la fin de la liste des arguments à transmettre au programme; finda besoin de cela car vous pouvez continuer avec plus d’arguments finddestiné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 findplutô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 -execcontourner les limites perçues . Cette -print0option 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;
\nest 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 lsdans un tableau? Définir IFS sur newline devrait résoudre les problèmes de caractères amusants dans les noms de fichiers; utiliser lspeut ê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, \nmais le réglage sur newline backspace fonctionne, comme suggéré ailleurs ici):
Par exemple (en supposant que le lsmodè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 lscommande est ls -t -U -r.
\nest 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