Рекурсивный скрипт bash для сбора информации о каждом файле в структуре каталогов
14
Как мне рекурсивно работать через дерево каталогов и выполнять определенную команду для каждого файла, и выводить путь, имя файла, расширение, размер файла и некоторый другой конкретный текст в один файл в bash.
лол, спасибо за редактирование; я буду первым, кто признает, что я слишком усложняю вещи, потому что я привык задавать 800 не относящихся к делу вопросов в мире людей; поэтому я стараюсь отвечать на очевидные вопросы; Я учусь, хотя :-)
SPooKYiNeSS
1
Хорошо, я думаю, вопрос довольно ясен о том, что должно быть сделано, просмотрите дерево каталогов и выведите информацию о каждом файле. Вопрос достаточно ясен, и, судя по количеству ответов, люди это понимают достаточно хорошо. 3 голоса за непонятность действительно не заслуживают этого вопроса
Сергей Колодяжный
Ответы:
16
Хотя findрешения просты и эффективны, я решил создать более сложное решение, основанное на этой интересной функции , которую я видел несколько дней назад.
Дополнительные объяснения и два других сценария, основанные на текущем, предоставлены здесь .
1. Создайте исполняемый файл скрипта, который называется walk, и который /usr/local/binбудет доступен для команды оболочки:
Скопируйте содержимое скрипта ниже и используйте в nano: Shift+ Insertдля вставки; Ctrl+ Oи Enterдля сохранения; 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 operationsfor entry in"$1"/*;do[[-f "$entry"]]&& file_specification;done# If the entry is a directory call walk() == create recursionfor 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()функции довольно хорошо описан Занной в ее ответе . Поэтому я опишу только новую часть.
В walk()функции я добавил этот цикл:
for entry in"$1"/*;do[[-f "$entry"]]&& file_specification;done
Это означает, что для каждого $entryфайла будет выполнена функция file_specification().
Функция file_specification()состоит из двух частей. Первая часть получает данные, связанные с файлом - имя, путь, размер и т. Д. Вторая часть выводит данные в хорошо отформатированном виде. Для форматирования данных используется команда printf. И если вы хотите настроить скрипт, вы должны прочитать об этой команде - например, эту статью .
Функция file_specification()является хорошим местом, где вы можете поместить конкретную команду, которая должна быть выполнена для каждого файла . Используйте этот формат:
команда "$ {entry}"
Или вы можете сохранить вывод команды как переменную, а затем printfэту переменную и т.д .:
MY_VAR = "$ ( команда " $ {entry} ")"
printf "% * s \ tFile size: \ t $ {YEL}% s $ {NCL} \ n" $ ((отступ + 4)) '' "$ MY_VAR"
Или непосредственно printfвывод команды:
printf "% * s \ tFile size: \ t $ {YEL}% s $ {NCL} \ n" $ ((отступ + 4)) '' "$ ( команда " $ {entry} ")"
Раздел с самого начала, называется Colourise the output, инициализирует несколько переменных, которые используются в printfкоманде для окрашивания вывода. Подробнее об этом вы можете найти здесь .
В конец скрипта добавлено дополнительное условие, которое касается абсолютных и относительных путей.
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
Какой процесс вы используете, чтобы сделать эти картинки @ pa4080?
pbhj
@pbhj, под Ubuntu я использую Peek, это просто и приятно, но иногда вылетает и не имеет возможностей редактирования. Большинство моих GIF-файлов создаются под Windows, где я записываю окно подключения VNC. У меня есть отдельная настольная машина, которую я в основном использую для создания MS Office и GIF :) Используемый там инструмент - ScreenToGif . Это с открытым исходным кодом, бесплатно, и имеет мощный редактор и механизм обработки. К сожалению, я не могу найти такой инструмент, как ScreenToGif для Ubuntu.
pa4080
13
Я немного озадачен тем, почему никто еще не опубликовал его, но действительно bashимеет рекурсивные возможности, если вы включите globstarопцию и используете **glob. Таким образом, вы можете написать (почти) чистый bash скрипт, который использует этот рекурсивный globstar, например так:
#!/usr/bin/env bash
shopt -s globstar
for i in./**/*doif[-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"fidone
Обратите внимание, что здесь мы используем расширение параметров, чтобы получить части имени файла, которые нам нужны, и мы не полагаемся на внешние команды, за исключением получения размера файла 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.
Если вы печатаете полное имя файла, что дает вам «расширение»? Возможно, вы действительно хотите получить информацию MIME, которая "$(file "$i")"(в приведенном выше сценарии как вторая часть printf) будет возвращаться?
pbhj
1
@pbhj лично для меня? Ничего. Но ОП, который задал вопрос, спросил output the path, filename, extension, filesize , поэтому ответ соответствует тому, что спрашивают. :)
Сергей Колодяжный
12
Ты можешь использовать find чтобы сделать работу
find /path/-type f -exec ls -alh {} \;
Это поможет вам, если вы просто хотите перечислить все файлы с размером.
-execпозволит вам выполнять пользовательские команды или сценарии для каждого файла,
\;используемого для анализа файлов один за другим, вы можете использовать, +;если хотите объединить их (имеется в виду имена файлов).
Элегантно и просто (если вы знаете о find -printf). +1
Дэвид Фёрстер
1
Если вы знаете, как глубоко дерево, самый простой способ будет использовать подстановочный знак * .
Запишите все, что вы хотите сделать, в виде сценария оболочки или функции
function thing(){...}
затем запустить for i in *; do thing "$i"; done, for i in */*; do thing "$i"; done... и т.д.
Внутри вашей функции / скрипта вы можете использовать несколько простых тестов, чтобы выделить файлы, с которыми вы хотите работать, и делать с ними все, что вам нужно.
«это не сработает, если в любом из ваших имен файлов есть пробелы» ... потому что вы забыли процитировать ваши переменные! Используйте «$ i» вместо $i.
Муру
@muru нет, причина, по которой он не работает, заключается в том, что цикл «for» разделяется на пробелы - » / 'превращается в разделенный пробелами список всех файлов. Вы можете обойти это, например, возиться с IFS, но в этот момент вы могли бы просто использовать find
Benubird
@ pa4080 не имеет отношения к этому ответу, но в любом случае это выглядит очень полезным, спасибо!
Benubird
Я думаю, вы не понимаете, как for i in */*работает. Вот, проверьте это:for i in */*; do printf "|%s|\n" "$i"; done
Ответы:
Хотя
find
решения просты и эффективны, я решил создать более сложное решение, основанное на этой интересной функции , которую я видел несколько дней назад.1. Создайте исполняемый файл скрипта, который называется
walk
, и который/usr/local/bin
будет доступен для команды оболочки:nano
: Shift+ Insertдля вставки; Ctrl+ Oи Enterдля сохранения; Ctrl+ Xза выход.2. Содержание скрипта
walk
:3. Объяснение:
Основной механизм
walk()
функции довольно хорошо описан Занной в ее ответе . Поэтому я опишу только новую часть.В
walk()
функции я добавил этот цикл:Это означает, что для каждого
$entry
файла будет выполнена функцияfile_specification()
.Функция
file_specification()
состоит из двух частей. Первая часть получает данные, связанные с файлом - имя, путь, размер и т. Д. Вторая часть выводит данные в хорошо отформатированном виде. Для форматирования данных используется командаprintf
. И если вы хотите настроить скрипт, вы должны прочитать об этой команде - например, эту статью .Функция
file_specification()
является хорошим местом, где вы можете поместить конкретную команду, которая должна быть выполнена для каждого файла . Используйте этот формат:Или вы можете сохранить вывод команды как переменную, а затем
printf
эту переменную и т.д .:Или непосредственно
printf
вывод команды:Раздел с самого начала, называется
Colourise the output
, инициализирует несколько переменных, которые используются вprintf
команде для окрашивания вывода. Подробнее об этом вы можете найти здесь .В конец скрипта добавлено дополнительное условие, которое касается абсолютных и относительных путей.
4. Примеры использования:
Чтобы запустить
walk
для текущего каталога:Чтобы запустить
walk
любой дочерний каталог:Чтобы запустить
walk
для любого другого каталога:Чтобы создать текстовый файл на основе
walk
вывода:Чтобы создать выходной файл без цветовых кодов ( источник ):
5. Демонстрация использования:
источник
Я немного озадачен тем, почему никто еще не опубликовал его, но действительно
bash
имеет рекурсивные возможности, если вы включитеglobstar
опцию и используете**
glob. Таким образом, вы можете написать (почти) чистыйbash
скрипт, который использует этот рекурсивный globstar, например так:Обратите внимание, что здесь мы используем расширение параметров, чтобы получить части имени файла, которые нам нужны, и мы не полагаемся на внешние команды, за исключением получения размера файла
du
и очистки вывода с помощьюawk
.И поскольку он пересекает ваше дерево каталогов, ваш вывод должен выглядеть примерно так:
Применяются стандартные правила использования скрипта: убедитесь, что он исполняемый с
chmod +x ./myscript.sh
и запускайте его из текущего каталога через./myscript.sh
или поместите~/bin
и запуститеsource ~/.profile
.источник
"$(file "$i")"
(в приведенном выше сценарии как вторая часть printf) будет возвращаться?output the path, filename, extension, filesize
, поэтому ответ соответствует тому, что спрашивают. :)Ты можешь использовать
find
чтобы сделать работуЭто поможет вам, если вы просто хотите перечислить все файлы с размером.
-exec
позволит вам выполнять пользовательские команды или сценарии для каждого файла,\;
используемого для анализа файлов один за другим, вы можете использовать,+;
если хотите объединить их (имеется в виду имена файлов).источник
С
find
только.Или вы можете использовать ниже:
источник
find -printf
). +1Если вы знаете, как глубоко дерево, самый простой способ будет использовать подстановочный знак
*
.Запишите все, что вы хотите сделать, в виде сценария оболочки или функции
затем запустить
for i in *; do thing "$i"; done
,for i in */*; do thing "$i"; done
... и т.д.Внутри вашей функции / скрипта вы можете использовать несколько простых тестов, чтобы выделить файлы, с которыми вы хотите работать, и делать с ними все, что вам нужно.
источник
$i
.for i in */*
работает. Вот, проверьте это:for i in */*; do printf "|%s|\n" "$i"; done
find
можно сделать это:Посмотрите на
man find
другие свойства файла.Если вам действительно нужно расширение, вы можете добавить это:
источник