bash itérer la liste des fichiers, sauf lorsquelle est vide

Je pensais que ce serait simple – mais cela savère plus complexe que prévu.

Je veux parcourir tous les fichiers dun type particulier dans un répertoire, donc jécris ceci:

#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done 

Cela fonctionne tant quil y a au moins un fichier correspondant dans le répertoire. Cependant, sil ny a pas de fichiers correspondants, jobtiens ceci:

current file is *.zip 

Jai ensuite essayé:

#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done 

Bien que le corps de la boucle ne sexécute pas lorsquil ny a pas de fichiers, jobtiens une erreur de ls:

ls: *.zip: No such file or directory 

Comment écrire boucle qui ne gère proprement aucun fichier correspondant?

Commentaires

  • Ajoutez shopt -s nullglob avant dexécuter la boucle for.
  • @cuolnglm: de manière effrayante, cela se traduit par ls renvoyant le nom du script en cours dexécution plutôt quune liste vide sur cette boîte RHEL5 (bash 3.2.25) si je fais FILES= ls * .zip ; for fname in "${FILES}"... mais cela fonctionne comme prévu avec for fname in *.zip ; do....
  • Utilisez for file in *.zip, pas `ls ...`. La suggestion de @cuonglm ' est telle que *.zip ne se développe en rien lorsque le motif ne ' ne correspond à aucun fichier. ls sans arguments répertorie le répertoire actuel.
  • Cette question explique pourquoi lanalyse de la sortie de ls doit généralement être évité: Pourquoi pas analyser ls? ; voir également le lien en haut de cette page vers larticle de BashGuide ' s ParsingLs .
  • Possibilité de duplication de Pourquoi mon script shell sétouffe-t-il avec des espaces ou dautres caractères spéciaux?

Réponse

Dans bash, vous pouvez définir loption nullglob pour quun modèle ne correspond à rien « disparaît », plutôt que dêtre traité comme une chaîne littérale:

shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done 

Dans le script shell POSIX, vous vérifiez simplement que fname existe (et en même temps avec [ -f ], vérifiez quil sagit dun fichier normal (ou dun lien symbolique vers un fichier normal) et non dautres types comme répertoire / fifo / périphérique. .):

for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done 

Remplacez [ -f "$fname" ] par [ -e "$fname" ] || [ -L "$fname ] si vous veulent boucler sur tous les fichiers (non cachés) dont le nom se termine par .zip indépendamment de leur type.

Remplacez *.zip par .*.zip .zip *.zip si vous souhaitez également prendre en compte les fichiers cachés dont le nom se termine par .zip.

Les commentaires

  • shopt -s nullglob nont pas fonctionné pour moi sur Ubuntu 17.04, mais [ -f "$fname" ] || continue a bien fonctionné.
  • @koppor On dirait que vous n’utilisez ' bash.
  • +1 pour une solution POSIX.

Réponse

set ./* #set the arg array to glob results ${2+":"} [ -e "$1" ] && #if more than one result skip the stat "$1" printf "current file is %s\n" "$@" #print the whole array at once ###or### ${2+":"} [ -e "$1" ] && #same kind of test for fname #iterate singly on $fname var for array do printf "file is %s\n" "$fname" #print each $fname for each iteration done 

Dans un commentaire ici, vous mentionnez lappel dune fonction …

file_fn() if [ -e "$1" ] || #check if first argument exists [ -L "$1" ] #or else if it is at least a broken link then for f #if so iterate on "$f" do : something w/ "$f" done else command <"${1-/dev/null}" #only fail w/ error if at least one arg fi file_fn * 

Réponse

Utilisez find

export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \; 

Vous DEVEZ exporter votre fonction shell avec export -f pour que cela fonctionne. Maintenant find exécute bash qui exécute votre fonction shell, et reste au niveau actuel du répertoire uniquement.

Commentaires

  • Qui se répète dans les sous-répertoires, et je veux appeler une fonction bash (pas un script) pour les correspondances.
  • @symcbean I ' ont été modifiés pour limiter à un seul répertoire et gérer les fonctions bash

Answer

À la place de:

FILES=`ls *.zip` 

Essayez:

FILES=`ls * | grep *.zip` 

De cette façon, si ls échoue (ce fait dans votre cas) il grep la sortie échouée et la renvoie comme une variable vide.

current file is <---Blank Here 

Vous pouvez ajouter un peu de logique à ceci pour le faire retourner « Non Fichier trouvé « 

#!/bin/bash FILES=`ls * | grep *.zip` if [[ $? == "0" ]]; then for fname in "$FILES" ; do echo current file is $fname done else echo "No Files Found" fi 

De cette façon, si la commande précédente réussissait (sortie avec une valeur 0) alors elle imprimera le fichier courant, sinon elle afficherait » Non Fichiers trouvés « 

Commentaires

  • I thi nk cest une mauvaise idée dajouter un autre processus (grep) plutôt que dessayer de résoudre le problème en utilisant un meilleur outil (find ) ou en modifiant le paramètre correspondant à la solution actuelle (avec shopt -s nullglob)
  • Selon le commentaire de lOP ' sur leur message dorigine, le shopt -s nullglob ne fonctionne pas. Jai essayé find tout en vérifiant ma réponse et cela a échoué. Je pense quà cause de ce que Dani a dit sur lexportation.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *