Як я працюю рекурсивно через дерево каталогів і виконую певну команду для кожного файлу, і виводите шлях, ім'я файлу, розширення, розмір файлів та якийсь інший конкретний текст до одного файлу в bash.
Як я працюю рекурсивно через дерево каталогів і виконую певну команду для кожного файлу, і виводите шлях, ім'я файлу, розширення, розмір файлів та якийсь інший конкретний текст до одного файлу в bash.
Відповіді:
Хоча 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і 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 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()функції досить добре описала Занна у своїй відповіді . Тож я опишу лише нову частину.
У межах 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 \ t Розмір файлу: \ t $ {YEL}% s $ {NCL} \ n" $ ((відступ + 4)) '' "$ MY_VAR"
Або безпосередньо printfвихід команди:
printf "% * s \ t Розмір файлу: \ t $ {YEL}% s $ {NCL} \ n" $ ((відступ + 4)) '' "$ ( команда " $ {запис} ")" Розділ випробовування, який називається 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.file5. Демонстрація використання:
Я трохи здивований, чому ще ніхто не опублікував це, але дійсно bashмає рекурсивні можливості, якщо ви включите globstarопцію та використовуєте **глобус. Таким чином, ви можете написати (майже) чистий bash скрипт, який використовує такий рекурсивний глобуляр на зразок цього:
#!/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.
"$(file "$i")"(у вищезазначеному сценарії як друга частина printf) поверталася?
output the path, filename, extension, filesize , тому відповідь відповідає тому, що задається. :)
Ви можете використовувати 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
find -printf). +1
Якщо ви знаєте, наскільки глибоко дерево, найпростішим способом буде використання магістра *.
Запишіть усе, що ви хочете зробити, як сценарій оболонки або функцію
function thing() { ... }
потім запустити 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