Muszę sprawdzić wszystkie podkatalogi i zgłosić, ile plików (bez dalszej rekurencji) zawierają:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Muszę sprawdzić wszystkie podkatalogi i zgłosić, ile plików (bez dalszej rekurencji) zawierają:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Odpowiedzi:
Robi to w bezpieczny i przenośny sposób. Nie pomylą go dziwne nazwy plików.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Zauważ, że najpierw wypisze liczbę plików, a następnie nazwę katalogu w osobnym wierszu. Jeśli chcesz zachować format OP, będziesz potrzebował dalszego formatowania, np
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Jeśli masz określony zestaw podkatalogów, którymi jesteś zainteresowany, możesz go zastąpić *
.
Dlaczego to jest bezpieczne? (a zatem godny skryptu)
Nazwy plików mogą zawierać dowolne znaki oprócz /
. Istnieje kilka znaków, które są traktowane specjalnie przez powłokę lub polecenia. Należą do nich spacje, znaki nowej linii i myślniki.
Korzystanie z for f in *
konstrukcji jest bezpiecznym sposobem uzyskania każdej nazwy pliku, bez względu na to, co zawiera.
Gdy masz nazwę pliku w zmiennej, nadal musisz unikać takich rzeczy find $f
. Gdyby $f
zawierała nazwę pliku -test
, find
narzekałaby na opcję, którą właśnie mu podałeś. Sposobem na uniknięcie tego jest użycie ./
przed nazwą; w ten sposób ma to samo znaczenie, ale nie zaczyna się już od myślnika.
Problemem są także nowe linie i spacje. Jeśli $f
zawiera nazwę pliku „witaj, kolego” find ./$f
, to jest find ./hello, buddy
. Mówisz, find
żeby spojrzeć na ./hello,
i buddy
. Jeśli te nie istnieją, narzeka i nigdy nie zajrzy ./hello, buddy
. Łatwo tego uniknąć - użyj cudzysłowów wokół zmiennych.
Wreszcie nazwy plików mogą zawierać znaki nowej linii, więc zliczanie nowych linii na liście nazw plików nie będzie działać; otrzymasz dodatkową liczbę dla każdej nazwy pliku z nową linią. Aby tego uniknąć, nie licz nowych linii na liście plików; zamiast tego policz znaki nowej linii (lub dowolny inny znak), które reprezentują pojedynczy plik. Dlatego find
polecenie ma po prostu -exec echo \;
i nie -exec echo {} \;
. Chcę wydrukować tylko jedną nową linię w celu liczenia plików.
-mindepth 1
-printf '\n'
zamiast -exec echo
.
-printf
, ale nie, jeśli chcesz, aby na przykład działało na FreeBSD.
Zakładając, że szukasz standardowego rozwiązania dla systemu Linux, stosunkowo prostym sposobem na osiągnięcie tego jest find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Gdzie find
przemierza dwa określone podkatalogi, do jednego -maxdepth
z 1, co zapobiega dalszej rekurencji i zgłasza tylko pliki ( -type f
) oddzielone znakami nowej linii. Wynik jest następnie przesyłany potokowo w wc
celu zliczenia liczby tych linii.
find . -maxdepth 1 -type d
wyjściem?
find $dirs ...
(b), jeśli znajdują się wyłącznie w jednym katalogu wyższego poziomu, glob z tego katalogu,find */ ...
-exec echo
do swojej komendy find - w ten sposób nie odbija echa nazwy pliku, tylko nowy wiersz.
Czy przez „bez rekurencji” rozumiesz, że jeśli directoryName1
ma podkatalogi, to nie chcesz liczyć plików w podkatalogach? Jeśli tak, oto sposób na policzenie wszystkich zwykłych plików we wskazanych katalogach:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Zauważ, że -f
test wykonuje dwie funkcje: sprawdza, czy pozycja pasująca do jednej z powyższych globów jest zwykłym plikiem, i sprawdza, czy pozycja była zgodna (jeśli jedna z globów nic nie pasuje, wzór pozostaje niezmieniony). Jeśli chcesz liczyć wszystkie wpisy w podanych katalogach, niezależnie od ich rodzaju, wymienić -f
się -e
.
Ksh ma sposób na dopasowanie wzorców do plików kropek i utworzenie pustej listy na wypadek, gdyby żaden plik nie pasował do wzorca. Więc w ksh możesz policzyć zwykłe pliki takie jak to:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
lub wszystkie pliki po prostu tak:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash ma różne sposoby na uproszczenie tego. Aby policzyć zwykłe pliki:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Aby policzyć wszystkie pliki:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Jak zwykle w Zsh jest jeszcze prostszy. Aby policzyć zwykłe pliki:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Zmień, (DN.)
aby (DN)
policzyć wszystkie pliki.
¹ Pamiętaj, że każdy wzorzec pasuje do siebie, w przeciwnym razie wyniki mogą być wyłączone (np. Jeśli liczysz pliki zaczynające się od cyfry, nie możesz tego zrobić, for x in [0-9]*; do if [ -f "$x" ]; then …
ponieważ może istnieć plik o nazwie [0-9]foo
).
Na podstawie skryptu zliczania , odpowiedzi Shawna i sztuczki Bash, aby upewnić się, że nawet nazwy plików z nowymi liniami są drukowane w użytecznej formie w jednym wierszu:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
polega na wydrukowaniu cytowanej wersji łańcucha, czyli ciągu jednowierszowego, który można umieścić w skrypcie Bash, aby interpretować go jako ciąg literalny zawierający (potencjalnie) znaki nowej linii i inne znaki specjalne. Na przykład, patrz echo -n $'\tfoo\nbar'
vs printf %q $'\tfoo\nbar'
.
find
Polecenie działa po prostu drukuje pojedynczy znak dla każdego pliku, a następnie licząc tych liniach zamiast liczenia.
Oto „brute-force” -ish sposób, aby uzyskać wynik, używając find
, echo
, ls
, wc
, xargs
i awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
kiedy zrobi to Bash?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: dla wszystkich katalogów policz liczbę wpisów w tym katalogu (w tym ukryte pliki kropek, z wyłączeniem.
i..
)