web-dev-qa-db-fra.com

Comment aller dans chaque répertoire et exécuter une commande?

Comment puis-je écrire un script bash qui parcourt chaque répertoire dans un répertoire parent_ et exécute une commande dans chaque répertoire.

La structure du répertoire est la suivante:

parent_directory (le nom peut être n'importe quoi - ne suit pas un modèle)

  • 001 (les noms de répertoire suivent ce modèle)
    • 0001.txt (les noms de fichiers suivent ce modèle)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

le nombre de répertoires est inconnu.

123
Van de Graff

Vous pouvez faire ce qui suit lorsque votre répertoire actuel est parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

Le ( et ) créer un sous-shell afin que le répertoire actuel ne soit pas modifié dans le script principal.

94
Mark Longair

Ceci réponse posté par Todd m'a aidé.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

La \( ! -name . \) évite d'exécuter la commande dans le répertoire en cours.

138
Christian Vielma

Si vous utilisez GNU find, vous pouvez essayer le paramètre -execdir, Par exemple:

find . -type d -execdir realpath "{}" ';'

ou (selon @ gniourf_gniourf comment ):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

Remarque: vous pouvez utiliser ${0#./} Au lieu de $0 Pour corriger ./ Au premier plan.

ou un exemple plus pratique:

find . -name .git -type d -execdir git pull -v ';'

Si vous voulez inclure le répertoire actuel, c'est encore plus simple en utilisant -exec:

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

ou en utilisant xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Ou exemple similaire suggéré par @ gniourf_gniourf :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

Les exemples ci-dessus prennent en charge les répertoires avec des espaces dans leur nom.


Ou en assignant à bash array:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

Remplacez . Par votre nom de dossier spécifique. Si vous n'avez pas besoin de courir de manière récursive, vous pouvez utiliser: dirs=(*) à la place. L'exemple ci-dessus ne prend pas en charge les répertoires avec des espaces.

Donc, comme @ gniourf_gniourf suggéré, le seul moyen approprié de placer la sortie de find dans un tableau sans utiliser de boucle explicite sera disponible dans Bash 4.4 avec:

mapfile -t -d '' dirs < <(find . -type d -print0)

Ou pas une manière recommandée (qui implique analyse de ls ):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

L'exemple ci-dessus ignorerait le répertoire en cours (comme demandé par OP), mais il se cassera sur les noms avec les espaces.

Voir également:

41
kenorb

Si le dossier de niveau supérieur est connu, vous pouvez simplement écrire quelque chose comme ceci:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

Sur le $ (jouez autant que vous voulez); vous pouvez mettre autant de code que vous voulez.

Notez que je n'ai "cd" sur aucun répertoire.

À votre santé,

19
gforcada

Vous pouvez y parvenir en effectuant un piping puis en utilisant xargs. Le problème est que vous devez utiliser le -I drapeau qui remplacera la sous-chaîne de votre commande bash par la sous-chaîne transmise par chacun des xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

Vous voudrez peut-être remplacer pwd par la commande que vous souhaitez exécuter dans chaque répertoire.

14
Piyush Singh
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done
10
Idelic

Alors que les liners sont bons pour une utilisation rapide et sale, je préfère une version plus détaillée pour écrire des scripts. C’est le modèle que j’utilise qui prend en charge de nombreux cas Edge et vous permet d’écrire du code plus complexe à exécuter sur un dossier. Vous pouvez écrire votre code bash dans la fonction dir_command. Ci-dessous, dir_coomand implémente le balisage de chaque référentiel dans git à titre d'exemple. Le reste du script appelle dir_command pour chaque dossier du répertoire. L’exemple d’itération d’un ensemble de dossiers donné est également inclus.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git Push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"
6
Shital Shah

vous pouvez utiliser

find .

pour rechercher tous les fichiers/répertoires dans le répertoire actuel récursif

Que vous pouvez diriger la sortie de la commande xargs comme si

find . | xargs 'command here'
4
Dan Bizdadea

Je ne comprends pas le problème avec la mise en forme du fichier, car vous ne voulez que parcourir les dossiers ... Cherchez-vous quelque chose comme ça?

cd parent
find . -type d | while read d; do
   ls $d/
done
3
Aif
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done
0
Fedir RYKHTIK