Les réponses acceptées / à vote élevé sont excellentes, mais elles manquent de quelques détails concrets. Cette publication couvre les cas sur la façon de mieux gérer lorsque l'expansion du nom de chemin du shell (glob) échoue, lorsque les noms de fichiers contiennent des sauts de ligne / symboles de tiret et le déplacement de la direction de sortie de la commande hors de la boucle for lors de l'écriture des résultats dans un fichier.
Lors de l'exécution de l'expansion du shell shell à l'aide de l'extension, *il est possible que l'expansion échoue s'il n'y a aucun fichier dans le répertoire et une chaîne glob non développée sera transmise à la commande à exécuter, ce qui pourrait avoir des résultats indésirables. Le bashshell fournit une option shell étendue pour cette utilisation nullglob. Ainsi, la boucle devient essentiellement la suivante dans le répertoire contenant vos fichiers
shopt -s nullglob
for file in ./*; do
cmdToRun [option] -- "$file"
done
Cela vous permet de quitter en toute sécurité la boucle for lorsque l'expression ./*ne renvoie aucun fichier (si le répertoire est vide)
ou d'une manière conforme POSIX ( nullglobest bashspécifique)
for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done
Cela vous permet d'aller à l'intérieur de la boucle lorsque l'expression échoue pour une fois et la condition [ -f "$file" ]vérifie si la chaîne non développée ./*est un nom de fichier valide dans ce répertoire, ce qui ne le serait pas. Donc, en cas d'échec de cette condition, en utilisant continuenous reprenons la forboucle qui ne s'exécutera pas par la suite.
Notez également l'utilisation de --juste avant de passer l'argument du nom de fichier. Cela est nécessaire car, comme indiqué précédemment, les noms de fichiers du shell peuvent contenir des tirets n'importe où dans le nom de fichier. Certaines des commandes shell l'interprètent et les traitent comme une option de commande lorsque le nom n'est pas cité correctement et exécute la commande en pensant si l'indicateur est fourni.
Le --signale la fin des options de ligne de commande dans ce cas, ce qui signifie que la commande ne doit pas analyser les chaînes au-delà de ce point en tant que drapeaux de commande, mais uniquement en tant que noms de fichiers.
La citation double des noms de fichiers résout correctement les cas où les noms contiennent des caractères globaux ou des espaces blancs. Mais les noms de fichiers * nix peuvent également contenir des retours à la ligne. Nous limitons donc les noms de fichiers avec le seul caractère qui ne peut pas faire partie d'un nom de fichier valide - l'octet nul ( \0). Comme il bashutilise en interne des Cchaînes de style dans lesquelles les octets nuls sont utilisés pour indiquer la fin de la chaîne, c'est le bon candidat pour cela.
Donc, en utilisant l' printfoption shell pour délimiter les fichiers avec cet octet NULL en utilisant l' -doption de readcommande, nous pouvons faire ci-dessous
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
Le nullglobet le printfsont entourés, (..)ce qui signifie qu'ils sont essentiellement exécutés dans un sous-shell (shell enfant), car pour éviter l' nullgloboption de réfléchir sur le shell parent, une fois la commande terminée. L' -d ''option de readcommande n'est pas compatible POSIX, a donc besoin d'un bashshell pour cela. En utilisant la findcommande, cela peut être fait comme
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
Pour les findimplémentations qui ne prennent pas en charge -print0(autres que les implémentations GNU et FreeBSD), cela peut être émulé en utilisantprintf
find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --
Un autre correctif important consiste à déplacer la redirection hors de la boucle for pour réduire un nombre élevé d'E / S de fichiers. Lorsqu'il est utilisé à l'intérieur de la boucle, le shell doit exécuter des appels système deux fois pour chaque itération de la boucle for, une fois pour ouvrir et une fois pour fermer le descripteur de fichier associé au fichier. Cela deviendra un goulot d'étranglement sur vos performances pour l'exécution de grandes itérations. La suggestion recommandée serait de le déplacer hors de la boucle.
En étendant le code ci-dessus avec ces correctifs, vous pouvez le faire
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out
qui placera essentiellement le contenu de votre commande pour chaque itération de votre entrée de fichier dans stdout et lorsque la boucle se terminera, ouvrez le fichier cible une fois pour écrire le contenu de la stdout et l'enregistrer. La findversion équivalente de la même serait
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out