Jak pracować rekurencyjnie przez drzewo katalogów i wykonać określoną komendę dla każdego pliku, i wysyłać ścieżkę, nazwę pliku, rozszerzenie, rozmiar pliku i jakiś inny określony tekst do jednego pliku w bash.
Jak pracować rekurencyjnie przez drzewo katalogów i wykonać określoną komendę dla każdego pliku, i wysyłać ścieżkę, nazwę pliku, rozszerzenie, rozmiar pliku i jakiś inny określony tekst do jednego pliku w bash.
Odpowiedzi:
Chociaż findrozwiązania są proste i wydajne, postanowiłem stworzyć bardziej skomplikowane rozwiązanie, oparte na tej interesującej funkcji , którą zobaczyłem kilka dni temu.
1. Utwórz plik skryptu wykonywalnego o nazwie walk, który znajduje się w, /usr/local/binaby był dostępny jako polecenie powłoki:
sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
nano: Shift+ Insertdo wklejania; Ctrl+ Oi Enterdla oszczędności; Ctrl+ Xdo wyjścia.2. Treść skryptu walkto:
#!/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. Objaśnienie:
Główny mechanizm tej walk()funkcji dość dobrze opisuje Zanna w swojej odpowiedzi . Opiszę więc tylko nową część.
W ramach walk()funkcji dodałem tę pętlę:
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
Oznacza to, że dla każdego $entrypliku będzie wykonywana funkcja file_specification().
Funkcja file_specification()składa się z dwóch części. Pierwsza część otrzymuje dane związane z plikiem - nazwa, ścieżka, rozmiar itp. Druga część generuje dane w dobrze sformatowanej formie. Do sformatowania danych służy polecenie printf. A jeśli chcesz ulepszyć skrypt, powinieneś przeczytać o tym poleceniu - na przykład w tym artykule .
Funkcja file_specification()jest dobrym miejscem, w którym można umieścić określone polecenie, które należy wykonać dla każdego pliku . Użyj tego formatu:
polecenie „$ {entry}”
Lub możesz zapisać wynik polecenia jako zmienną, a następnie printftę zmienną itp .:
MY_VAR = "$ ( polecenie " $ {entry} ")"
printf "% * s \ t Rozmiar pliku: \ t $ {YEL}% s $ {NCL} \ n" $ ((wcięcie + 4)) '' „$ MY_VAR”
Lub bezpośrednio printfwynik polecenia:
printf "% * s \ t Rozmiar pliku: \ t $ {YEL}% s $ {NCL} \ n" $ ((wcięcie + 4)) '' „$ ( polecenie „ $ {entry} ”)” Sekcja zwana żebraniem Colourise the outputinicjuje kilka zmiennych, które są używane w printfpoleceniu do kolorowania danych wyjściowych. Więcej na ten temat można znaleźć tutaj .
Do dolnej części skryptu dodano dodatkowy warunek, który dotyczy ścieżek bezwzględnych i względnych.
4. Przykłady użycia:
Aby uruchomić walkbieżący katalog:
walk # You shouldn't use any argument,
walk ./ # but you can use also this formatAby uruchomić walkdowolny katalog podrzędny:
walk <directory name>
walk ./<directory name>
walk <directory name>/<sub directory>Aby uruchomić walkdla dowolnego innego katalogu:
walk /full/path/to/<directory name>Aby utworzyć plik tekstowy na podstawie danych walkwyjściowych:
walk > output.fileAby utworzyć plik wyjściowy bez kodów kolorów ( źródło ):
walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file5. Demonstracja użytkowania:
Jestem nieco zakłopotany, dlaczego nikt jeszcze tego nie opublikował, ale rzeczywiście bashma możliwości rekurencyjne, jeśli włączysz globstaropcję i użyjesz **glob. Jako taki, możesz napisać (prawie) czysty bash skrypt, który używa tej rekurencyjnej globstar w następujący sposób:
#!/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
Zauważ, że tutaj używamy rozszerzania parametrów, aby uzyskać pożądane części nazwy pliku i nie polegamy na poleceniach zewnętrznych, z wyjątkiem uzyskiwania rozmiaru pliku dui czyszczenia wyniku awk.
Gdy przegląda drzewo katalogów, dane wyjściowe powinny wyglądać mniej więcej tak:
Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326
chmod +x ./myscript.shObowiązują standardowe zasady korzystania ze skryptu: upewnij się, że jest wykonywalny i uruchom go z bieżącego katalogu przez ./myscript.shlub umieść w ~/bini uruchom source ~/.profile.
"$(file "$i")"(w powyższym skrypcie jako druga część printf) powrócą?
output the path, filename, extension, filesize , więc odpowiedź odpowiada zadanemu pytaniu. :)
Możesz użyć finddo wykonania pracy
find /path/ -type f -exec ls -alh {} \;
Pomoże Ci to, jeśli chcesz tylko wyświetlić listę wszystkich plików o rozmiarze.
-execpozwoli ci wykonać niestandardowe polecenie lub skrypt dla każdego pliku
\;użytego do parsowania plików jeden po drugim, możesz użyć, +;jeśli chcesz je połączyć (oznacza nazwy plików).
Z findtylko.
find /path/ -type f -printf "path:%h fileName:%f size:%kKB Some Text\n" > to_single_file
Lub możesz zamiast tego użyć poniżej:
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
Jeśli wiesz, jak głębokie jest drzewo, najłatwiejszym sposobem będzie użycie symbolu wieloznacznego *.
Napisz wszystko, co chcesz zrobić, jako skrypt powłoki lub funkcja
function thing() { ... }
następnie uruchomić for i in *; do thing "$i"; done, for i in */*; do thing "$i"; done... etc
W ramach funkcji / skryptu możesz użyć kilku prostych testów, aby wyodrębnić pliki, z którymi chcesz pracować, i zrobić z nimi wszystko, czego potrzebujesz.
$i.
for i in */*działa. Tutaj przetestuj:for i in */*; do printf "|%s|\n" "$i"; done