Рекурсивный сценарий удара для сбора информации о каждом файле в структуре каталогов

Как я работаю рекурсивно через дерево каталогов и выполняю определенную команду на каждом файле и произвожу путь, имя файла, расширение, размер файла и некоторый другой определенный текст в единственный файл в ударе.

14
задан 23 January 2018 в 03:36

6 ответов

В то время как find решения просты и мощны, я решил создать более сложное решение, которое основано на этой интересной функции, которую я видел несколько дней назад.

  • Больше объяснений и два других сценария, на основе тока обеспечиваются здесь.

1. Создайте исполняемый файл сценария, названный walk, это расположено в /usr/local/bin быть доступным как команда оболочки:

sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
  • Скопируйте ниже содержания сценария и используйте в nano: Shift+Insert для вставки; Ctrl+O и Вводят, сохраняют; Ctrl+X для выхода.

2. Содержание сценария walk :

#!/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. Объяснение:

  • Основной механизм walk() функция вполне прилично описана Zanna в ее ответе. Таким образом, я опишу только новую часть.

  • В walk() функция я добавил этот цикл:

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

    Это означает для каждого $entry это - файл, будет выполняться функция file_specification().

  • Функция file_specification() имеет две части. Первая часть связала данные с файлом - имя, путь, размер, и т.д. Вторая часть произвела данные в хорошо отформатированной форме. Отформатировать данные используется команда printf. И если Вы хотите настроить сценарий, необходимо читать об этой команде - например, эта статья.

  • Функция file_specification() хорошее место, куда можно поместить определенную команду, которая должна быть, выполняются для каждого файла. Используйте этот формат:

    command "${entry}"

    Или можно сохранить вывод команды как переменная, и затем printf эта переменная, и т.д.:

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

    Или непосредственно printf вывод команды:

    printf "%*s\tFile size:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$(command "${entry}")"
  • Раздел к просьбе, названной Colourise the output, инициализируйте немного переменных, которые используются в printf управляйте к colourise выводом. Больше об этом Вы могли найти здесь.

  • К нижней части документа на получение добавленное дополнительное условие, которое имеет дело с полными и относительными путями.

4. Примеры использования:

  • Работать walk для текущего каталога:

    walk      # You shouldn't use any argument, 
    walk ./   # but you can use also this format
    
  • Работать walk для любого дочернего каталога:

    walk <directory name>
    walk ./<directory name>
    walk <directory name>/<sub directory>
    
  • Работать walk для любого другого каталога:

    walk /full/path/to/<directory name>
    
  • Создать текстовый файл, на основе walk вывод:

    walk > output.file
    
  • Создать выходной файл без цветовых кодов (источник):

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

5. Демонстрация использования:

enter image description here

15
ответ дан 23 November 2019 в 02:53

Я немного озадачен относительно того, почему никто еще не отправил его, но действительно bash действительно имеет рекурсивные возможности, если Вы включаете globstar опция и использование ** шарик. По сути, можно записать (почти) чистый bash сценарий, который использует тот рекурсивный globstar как это:

#!/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

Заметьте, что здесь мы используем расширение параметра для получения частей имени файла, которое мы хотим, и мы не полагаемся на внешние команды за исключением получения размера файла с du и очистка вывода с awk.

И поскольку это пересекает Ваше дерево каталогов, Ваш вывод должен что-то вроде этого:

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

Стандартные правила использования сценария применяются: удостоверьтесь, что это - исполняемый файл с chmod +x ./myscript.sh и выполненный это из текущего каталога через ./myscript.sh или поместите его в ~/bin и выполненный source ~/.profile.

13
ответ дан 23 November 2019 в 02:53

Можно использовать find сделать задание

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

Это поможет Вам, если Вы просто захотите перечислить все файлы с размером.

-exec позволит Вам выполнять пользовательскую команду или сценарий для каждого файла \; используемый для парсинга файлов один за другим можно использовать +; если Вы хотите связать их (означает имена файлов).

12
ответ дан 23 November 2019 в 02:53

С find только.

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

Или, Вы могли использовать ниже вместо этого:

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
ответ дан 23 November 2019 в 02:53

Если Вы знаете, как глубоко дерево, самый легкий путь будет состоять в том, чтобы использовать подстановочный знак *.

Опишите все, что Вы хотите сделать как сценарий оболочки или функция

function thing() { ... }

затем выполненный for i in *; do thing "$i"; done, for i in */*; do thing "$i"; done... и т.д.

В Вашей функции/сценарии можно использовать некоторые простые тесты для выбора файлов, с которыми Вы хотите работать и сделать независимо от того, что Вы должны с ними.

1
ответ дан 23 November 2019 в 02:53

find может сделать это:

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

Взгляните на man find для других свойств файла.

При реальной необходимости в расширении можно добавить это:

find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;
1
ответ дан 23 November 2019 в 02:53

Другие вопросы по тегам:

Похожие вопросы: