Как я работаю рекурсивно через дерево каталогов и выполняю определенную команду на каждом файле и произвожу путь, имя файла, расширение, размер файла и некоторый другой определенный текст в единственный файл в ударе.
В то время как 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. Демонстрация использования:
Я немного озадачен относительно того, почему никто еще не отправил его, но действительно 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
.
Можно использовать find
сделать задание
find /path/ -type f -exec ls -alh {} \;
Это поможет Вам, если Вы просто захотите перечислить все файлы с размером.
-exec
позволит Вам выполнять пользовательскую команду или сценарий для каждого файла \;
используемый для парсинга файлов один за другим можно использовать +;
если Вы хотите связать их (означает имена файлов).
С 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
Если Вы знаете, как глубоко дерево, самый легкий путь будет состоять в том, чтобы использовать подстановочный знак *
.
Опишите все, что Вы хотите сделать как сценарий оболочки или функция
function thing() { ... }
затем выполненный for i in *; do thing "$i"; done
, for i in */*; do thing "$i"; done
... и т.д.
В Вашей функции/сценарии можно использовать некоторые простые тесты для выбора файлов, с которыми Вы хотите работать и сделать независимо от того, что Вы должны с ними.
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"' {} \;