15 votes

Bash script récursif pour collecter des informations sur chaque fichier dans une structure de répertoire.

Comment puis-je travailler récursivement à travers une arborescence de répertoires et exécuter une commande spécifique sur chaque fichier, et sortir le chemin, le nom de fichier, l'extension, la taille du fichier et d'autres textes spécifiques dans un seul fichier en bash.

19voto

pa4080 Points 27038

Alors que find sont simples et puissantes, j'ai décidé de créer une solution plus compliquée, qui se base sur cette fonction intéressante que j'ai vu il y a quelques jours.

  • Plus d'explications et deux autres scripts, basés sur le courant sont fournis aquí .

1. Créer un fichier exécutable script, appelé walk qui est situé dans /usr/local/bin pour être accessible en tant que commande Shell :

sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
  • Copier le contenu du script ci-dessous et l'utiliser dans nano : Shift + Insert pour la pâte ; Ctrl + O y Enter pour sauver ; Ctrl + X pour la sortie.

2. Le contenu du script walk est :

#!/bin/bash

# Colourise the output
RED='\033[0;31m'        # Red
GRE='\033[0;32m'        # Green
YEL='\033[1;33m'        # Yellow
NCL='\033[0m'           # No Color

file_specification() {
        FILE_NAME="$(basename "${entry}")"
        DIR="$(dirname "${entry}")"
        NAME="${FILE_NAME%.*}"
        EXT="${FILE_NAME##*.}"
        SIZE="$(du -sh "${entry}" | cut -f1)"

        printf "%*s${GRE}%s${NCL}\n"                    $((indent+4)) '' "${entry}"
        printf "%*s\tFile name:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$FILE_NAME"
        printf "%*s\tDirectory:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$DIR"
        printf "%*s\tName only:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$NAME"
        printf "%*s\tExtension:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$EXT"
        printf "%*s\tFile size:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$SIZE"
}

walk() {
        local indent="${2:-0}"
        printf "\n%*s${RED}%s${NCL}\n\n" "$indent" '' "$1"
        # If the entry is a file do some operations
        for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
        # If the entry is a directory call walk() == create recursion
        for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+4)); done
}

# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"      
echo                    

3. Explication :

  • Le principal mécanisme de l walk() est assez bien décrite par Zanna dans son livre intitulé réponse . Je ne décrirai donc que la nouvelle partie.

  • Au sein de la walk() j'ai ajouté cette boucle :

    for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done

    Cela signifie que pour chaque $entry c'est-à-dire un fichier sera exécuté la fonction file_specification() .

  • La fonction file_specification() comporte deux parties. La première partie récupère les données relatives au fichier - nom, chemin, taille, etc. La deuxième partie produit les données sous une forme bien formatée. Pour formater les données, on utilise la commande printf . Et si vous voulez modifier le script vous devriez lire cette commande - par exemple cet article .

  • La fonction file_specification() est un bon endroit où vous pouvez mettre la commande spécifique qui doit être exécutée pour chaque fichier . Utilisez ce format :

    command "${entry}"

    Ou vous pouvez enregistrer la sortie de la commande comme variable, puis printf cette variable, etc :

    MY\_VAR="$(_command_ "${entry}")"
    printf "%\*s\\tFile size:\\t${YEL}%s${NCL}\\n" $((indent+4)) '' "$MY\_VAR"

    Ou directement printf la sortie de la commande :

    printf "%\*s\\tFile size:\\t${YEL}%s${NCL}\\n" $((indent+4)) '' "$(_command_ "${entry}")"
  • La section de la mendicité, appelée Colourise the output initialiser quelques variables qui sont utilisées dans l'application printf pour colorer la sortie. Pour en savoir plus, vous pouvez consulter aquí .

  • Au bas du script est ajoutée une condition supplémentaire qui traite des chemins absolus et relatifs.

4. Exemples d'utilisation :

  • Pour exécuter walk pour le répertoire courant :

    walk      # You shouldn't use any argument, 
    walk ./   # but you can use also this format
  • Pour exécuter walk pour tout répertoire enfant :

    walk <directory name>
    walk ./<directory name>
    walk <directory name>/<sub directory>
  • Pour exécuter walk pour tout autre répertoire :

    walk /full/path/to/<directory name>
  • Pour créer un fichier texte, basé sur le walk sortie :

    walk > output.file
  • Pour créer un fichier de sortie sans codes de couleur ( source ) :

    walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file

5. Démonstration de l'utilisation :

enter image description here

14voto

Sergiy Kolodyazhnyy Points 97292

Je suis un peu perplexe quant à la raison pour laquelle personne ne l'a encore posté, mais en effet bash a des capacités récursives, si vous activez la fonction globstar et utiliser l'option ** glob. En tant que tel, vous pouvez écrire (presque) de pures bash script qui utilise ce globstar récursif comme ceci :

#!/usr/bin/env bash

shopt -s globstar

for i in ./**/*
do
    if [ -f "$i" ];
    then
        printf "Path: %s\n" "${i%/*}" # shortest suffix removal
        printf "Filename: %s\n" "${i##*/}" # longest prefix removal
        printf "Extension: %s\n"  "${i##*.}"
        printf "Filesize: %s\n" "$(du -b "$i" | awk '{print $1}')"
        # some other command can go here
        printf "\n\n"
    fi
done

Remarquez qu'ici nous utilisons l'expansion des paramètres pour obtenir les parties du nom de fichier que nous voulons et nous ne nous appuyons pas sur des commandes externes sauf pour obtenir la taille du fichier avec du et la sortie de nettoyage avec awk .

Et comme il traverse votre arborescence de répertoires, votre sortie devrait ressembler à ceci :

Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326

Les règles standard d'utilisation de script s'appliquent : assurez-vous qu'il est exécutable avec chmod +x ./myscript.sh et l'exécuter depuis le répertoire courant via ./myscript.sh ou le placer dans ~/bin et exécuter source ~/.profile .

12voto

Rajesh Rajendran Points 997

Vous pouvez utiliser find pour faire le travail

find /path/ -type f -exec ls -alh {} \;

Cela vous aidera si vous voulez juste lister tous les fichiers avec leur taille.

-exec vous permettra d'exécuter une commande personnalisée ou script pour chaque fichier \; utilisé pour analyser les fichiers un par un, vous pouvez utiliser +; si vous voulez les concaténer (signifie noms de fichiers).

6voto

αғsнιη Points 33236

Avec find seulement.

find /path/ -type f -printf "path:%h  fileName:%f  size:%kKB Some Text\n" > to_single_file

Ou bien, vous pouvez utiliser la méthode ci-dessous à la place :

find -type f -not -name "to_single_file"  -execdir sh -c '
    printf "%s %s %s %s Some Text\n" "$PWD" "${1#./}" "${1##*.}" $(stat -c %s "$1")
' _ {} \; > to_single_file

1voto

Oliver Salzburg Points 855

Si vous savez quelle est la profondeur de l'arbre, le plus simple sera d'utiliser le joker * .

Écrire tout ce que vous voulez faire comme un Shell Shell ou une fonction.

function thing() { ... }

puis exécutez for i in *; do thing "$i"; done , for i in */*; do thing "$i"; done , ... etc.

À l'intérieur de votre fonction/script, vous pouvez utiliser quelques des tests simples pour sélectionner les fichiers sur lesquels vous voulez travailler et faire ce que vous voulez avec eux.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X