Parcourez tous les sous-répertoires et faites quelque chose dans le script shell Unix

17

Je veux que mon script shell visite tous les sous-répertoires d'un répertoire principal. Faites quelque chose dans les répertoires, envoyez la sortie vers un fichier spool et passez au répertoire suivant. Considérez Dir principal = / tmp Sous Dir = ABCD (quatre sous-répertoires)

Ashish
la source
2
OK, montrez-nous votre script jusqu'à présent. Quelle partie vous cause des problèmes?
terdon

Réponses:

22

Utilisez une forboucle:

for d in $(find /path/to/dir -maxdepth 1 -type d)
do
  #Do something, the directory is accessible with $d:
  echo $d
done >output_file

Il recherche uniquement les sous-répertoires du répertoire /path/to/dir. Notez que l'exemple simple ci-dessus échouera si les noms de répertoire contiennent des espaces ou des caractères spéciaux. Une approche plus sûre consiste à:

find /tmp -maxdepth 1 -type d -print0 |
  while IFS= read -rd '' dir; do echo "$dir"; done

Ou en clair bash:

for d in /path/to/dir/*; do
  if [ -d "$d" ]; then
    echo "$d"
  fi
done

(notez que contrairement à findcela, on considère également les liens symboliques vers les répertoires et exclut ceux cachés)

le chaos
la source
1
au moins pointer les limites et les risques associés au traitement de la sortie de findcomme ça.
Stéphane Chazelas
Salut ... j'ai essayé d'exécuter ci-dessous pour la boucle pour d dans $ (find / backup / ASHISH -maxdepth 1 -type d) do ls -l | awk '{print $ 9}' | grep CC * _ date +"%m%d20%Y"| xargs echo echo echo $ d
Ashish
Salut ... J'ai essayé ci-dessous pour la boucle. pour d dans $ (find / backup / ASHISH -maxdepth 1 -type d) do ls -l | awk '{print $ 9}' | grep CC * _ date +"%m%d20%Y"| xargs echo echo $ d Le résultat attendu est ls -ltr de tous les sous-répertoires . La boucle ci-dessus ne fonctionne pas
Ashish
1

Je suis un bashdébutant complet , mais un vétéran UN * X. Bien que cela puisse sans doute être fait dans les scripts shell Bash, dans le passé, nous avions l'habitude find [-maxdepth <levels>] <start-dir> -exec <command> ;de le faire. Vous pourriez en faire un man findet jouer, peut-être jusqu'à ce que quelqu'un vous dise comment le faire bash!

JonBrave
la source
Je suis très flatté que ma réponse "sommaire" ait reçu un vote positif. Cependant, pourquoi la réponse de @chaos ci-dessous a-t-elle reçu un vote négatif? (En tant que nouveau venu sur ce forum, je ne peux pas poster ce commentaire contre sa réponse, seulement contre la mienne.) Sa deuxième suggestion est correcte pour une solution de script shell, et évite la surcharge d'exécution d'une findcommande externe .
JonBrave
Son second est en effet correct. Son premier échouera si les noms de répertoire contiennent des espaces ou des caractères spéciaux (barres obliques inverses par exemple). Voir la modification que j'ai apportée à sa réponse pour la version sûre.
terdon
Je suis d'accord. C'était sa deuxième réponse, que je louais.
JonBrave
Je sais, je venais d'expliquer le downvote (que je n'ai pas jeté).
terdon
Je l'ai jeté. La $(find...)chose est une mauvaise pratique .
Stéphane Chazelas
1

On dirait que vous voulez les noms de fichiers sous chacun des sous-répertoires; le ls -l | awkn'est pas assez robuste, et si ces noms de fichiers comprennent des espaces et / ou des retours à la ligne? Ce qui suit findfonctionnerait même pour findles personnes qui n'ont pas le choix -maxdepth:

find . ! -name . -type d -prune -exec sh -c '
   cd "$1" && \
   find "." ! -name . -prune  -type f
' {} {} \;

la source
0

J'ai la solution. La commande find ci-dessous répond à mes besoins.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && ls -l |awk '{ print $9 }' |grep `date +"%m%d%Y"`|xargs echo" \;
Ashish
la source
0

Il est également possible d'utiliser ls, grep et tr

for dir in $(ls -1FA | grep / | tr -d /); do echo $dir/something; done

ls -1FA | grep / | tr -d / | while IFS= read -r TD; do echo $TD/something; done

du / sed peut également être utilisé comme sélecteur si votre ls n'a pas les options ci-dessus

du --max-depth=1 | sed -e 's/^.*\.\///' | grep -v '\.$'

Il peut être important de noter que ces exemples renvoient des répertoires cachés et excluent les répertoires parent et actuels

JGurtz
la source
1
(1)  lsécrit un fichier par ligne (ce que l' -1option spécifie) par défaut lorsque la sortie standard est un tube (donc c'est superflu dans vos réponses). (2) L'analyse de la sortie de lsest une mauvaise idée - voir ceci et cela . Votre première réponse échouera si les répertoires ont des espaces (ou des retours à la ligne) dans leurs noms, et tous échoueront s'ils ont des retours à la ligne dans leurs noms. (3) Vous devez toujours citer les variables shell (par exemple, "$dir") sauf si vous avez une bonne raison de ne pas le faire, et vous êtes sûr de savoir ce que vous faites.
Scott
Bons points Scott. Laissant la réponse comme, pour de nombreux systèmes sans dirs nommés de manière flagrante, je pense que cela peut être utile pour les versions uniques.
JGurtz