399 votes

Exécuter la commande sur tous les fichiers d'un répertoire

Quelqu'un pourrait-il fournir le code permettant de faire ce qui suit ? Supposons qu'il existe un répertoire de fichiers, qui doivent tous être exécutés par un programme. Le programme sort les résultats en sortie standard. J'ai besoin d'un script qui ira dans un répertoire, exécutera la commande sur chaque fichier, et conciliera la sortie dans un grand fichier de sortie.

Par exemple, pour exécuter la commande sur 1 fichier :

$ cmd [option] [filename] > results.out

3 votes

Je voudrais ajouter à la question. Peut-on le faire en utilisant xargs ? par exemple, ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out

3 votes

C'est possible, mais vous avez probablement ne veulent pas utiliser ls pour conduire xargs . Si cmd est écrit de manière compétente, peut-être pouvez-vous simplement faire cmd <wildcard> .

6voto

Rahul Points 6

Une méthode rapide et sale qui permet parfois de faire le travail est la suivante :

find directory/ | xargs  Command 

Par exemple pour trouver le nombre de lignes dans tous les fichiers du répertoire courant, vous pouvez faire :

find . | xargs wc -l

8 votes

@Hubert Pourquoi avez-vous des sauts de ligne dans vos noms de fichiers ? !

2 votes

Ce n'est pas une question de "pourquoi", c'est une question d'exactitude - les noms de fichiers n'ont pas besoin d'inclure des caractères imprimables, ils n'ont même pas besoin d'être des séquences UTF-8 valides. De plus, ce qu'est une nouvelle ligne dépend beaucoup de l'encodage, un encodage est la nouvelle ligne d'un autre. Voir la page de code 437

3 votes

Allez, vraiment ? Ça marche dans 99,9% des cas, et il a dit "rapide et sale".

1voto

tuxdna Points 1835

Basé sur l'approche de @Jim Lewis :

Voici une solution rapide utilisant find ainsi que le tri des fichiers par leur date de modification :

$ find  directory/ -maxdepth 1 -type f -print0 | \
  xargs -r0 stat -c "%y %n" | \
  sort | cut -d' ' -f4- | \
  xargs -d "\n" -I{} cmd -op1 {} 

Pour le triage, voir :

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time

0 votes

Cela ne fonctionnera pas si les fichiers ont des sauts de ligne dans leurs noms.

2 votes

@HubertKario Vous voulez peut-être en savoir plus sur -print0 pour find y -0 pour xargs qui utilise le caractère nul à la place de tout espace (y compris les nouvelles lignes).

0 votes

Oui, en utilisant -print0 est quelque chose qui aide, mais l'ensemble du pipeline doit utiliser quelque chose comme ça, et sort n'est pas

1voto

Eric Wooley Points 586

J'avais besoin de copier tous les fichiers .md d'un répertoire à un autre, alors voici ce que j'ai fait.

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

Ce qui est assez difficile à lire, alors décomposons-le.

Commencez par vous rendre dans le répertoire contenant vos fichiers,

for i in **/*.md; pour chaque fichier de votre modèle

mkdir -p ../docs/"$i" faites ce répertoire dans un dossier docs en dehors du dossier contenant vos fichiers. Ce qui crée un dossier supplémentaire avec le même nom que ce fichier.

rm -r ../docs/"$i" supprimer le dossier supplémentaire qui est créé à la suite de mkdir -p

cp "$i" "../docs/$i" Copier le fichier actuel

echo "$i -> ../docs/$i" Echo de ce que vous avez fait

; done Vivre heureux pour toujours

0 votes

Note : pour ** pour travailler, le globstar L'option shell doit être définie : shopt -s globstar

1voto

Chetabahana Points 3161

Profondeur maximale

J'ai trouvé que cela fonctionne bien avec La réponse de Jim Lewis il suffit d'ajouter un peu comme ça :

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out

Ordre de tri

Si vous voulez l'exécuter dans l'ordre de tri, modifiez-le comme ceci :

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out

A titre d'exemple, cette opération sera exécutée dans l'ordre suivant :

bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh

Profondeur illimitée

Si vous voulez exécuter une profondeur illimitée sous certaines conditions, vous pouvez utiliser cette méthode :

export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out

puis mettre au dessus de chaque fichier dans les répertoires enfants comme ceci :

#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return

et quelque part dans le corps du fichier parent :

if <a condition is matched>
then
    #execute child files
    export DIR=`pwd`
fi

-1voto

yovie Points 85

Je pense que la solution simple est :

sh /dir/* > ./result.txt

3 votes

Avez-vous bien compris la question ? Cela va simplement essayer d'exécuter chaque fichier du répertoire à travers le shell - comme si c'était un script.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X