web-dev-qa-db-fra.com

Script bash récursif pour collecter des informations sur chaque fichier dans une structure de répertoires

Comment travailler de manière récursive dans une arborescence de répertoires et exécuter une commande spécifique sur chaque fichier et générer le chemin, le nom du fichier, l'extension, la taille du fichier et un autre texte spécifique dans un seul fichier dans bash.

13
SPooKYiNeSS

Alors que les solutions find sont simples et puissantes, j'ai décidé de créer une solution plus complexe, basée sur cette fonction intéressante , que j'ai vue il y a quelques jours.

  • Plus d'explications et deux autres scripts, basés sur l'actuel, sont fournis ici .

1. Créez un fichier de script exécutable, appelé walk, situé dans /usr/local/bin afin qu'il soit 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
  • Copiez le contenu du script ci-dessous et utilisez-le dans nanoShift+Insert pour la pâte; Ctrl+O et 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 mécanisme principal de la fonction walk() est assez bien décrit par Zanna dans son réponse . Je ne décrirai donc que la nouvelle partie.

  • Dans la fonction walk() j'ai ajouté cette boucle:

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

    Cela signifie que pour chaque $entry qui est un fichier, la fonction file_specification() sera exécutée.

  • La fonction file_specification() a deux parties. La première partie récupère les données liées au fichier - nom, chemin d'accès, taille, etc. La seconde partie affiche les données sous une forme bien formatée. Pour formater les données, utilisez la commande printf. Et si vous voulez modifier le script, vous devriez en savoir plus sur cette commande - par exemple cet article .

  • La fonction file_specification() est un bon endroit où vous pouvez mettre la commande spécifique à exécuter pour chaque fichier . Utilisez ce format:

    commander "$ {entry}"

    Ou vous pouvez enregistrer le résultat de la commande sous forme de variable, puis printf cette variable, etc.:

    MY_VAR = "$ (commander "$ {entry}") "
     printf"% * s\t Taille du fichier:\t $ {YEL}% s $ {NCL}\n "$ ((indent + 4)) ''" $ MY_VAR "

    Ou directement printf le résultat de la commande:

    printf "% * s\t Taille du fichier:\t $ {YEL}% s $ {NCL}\n" $ ((indent + 4)) '' "$ (commander "$ {entry}") "
  • La section de début, appelée Colourise the output, initialise quelques variables utilisées dans la commande printf pour coloriser la sortie. Plus à ce sujet, vous pouvez trouver ici .

  • 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 actuel:

    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 la sortie walk:

    walk > output.file
    
  • Pour créer un fichier de sortie sans code 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

13
pa4080

Je suis un peu perplexe quant à la raison pour laquelle personne ne l'a encore publiée, mais bash a effectivement des fonctionnalités récursives, si vous activez l'option globstar et utilisez ** glob. En tant que tel, vous pouvez écrire un script (presque) pur bash qui utilise cet 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

Notez que nous utilisons ici le développement de paramètres pour obtenir les parties de nom de fichier souhaitées et que nous ne comptons pas sur des commandes externes, à l'exception de la taille du fichier avec du et du nettoyage de la sortie 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 du script s'appliquent: assurez-vous qu'il est exécutable avec chmod +x ./myscript.sh et exécutez-le à partir du répertoire en cours via ./myscript.sh ou placez-le dans ~/bin et exécutez source ~/.profile.

12

Vous pouvez utiliser find pour faire le travail

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

Cela vous aidera si vous souhaitez simplement répertorier tous les fichiers de taille.

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

12
Rajesh Rajendran

Avec find uniquement.

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

Ou, vous pouvez utiliser 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
6
αғsнιη

Si vous connaissez la profondeur de l’arbre, le moyen le plus simple consiste à utiliser le caractère générique *.

Écrivez tout ce que vous voulez faire en tant que script shell ou fonction

function thing() { ... }

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

Dans votre fonction/script, vous pouvez utiliser quelques tests simples pour distinguer les fichiers avec lesquels vous souhaitez travailler et en faire le nécessaire.

1
Benubird

find peut faire ceci:

find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\n'

Regardez man find pour d'autres propriétés de fichier.

Si vous avez vraiment besoin de l'extension, vous pouvez ajouter ceci:

find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;
1
Katu