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 $fzawierała nazwę pliku -test, findnarzekał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 $fzawiera 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 findpolecenie 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 findprzemierza dwa określone podkatalogi, do jednego -maxdepthz 1, co zapobiega dalszej rekurencji i zgłasza tylko pliki ( -type f) oddzielone znakami nowej linii. Wynik jest następnie przesyłany potokowo w wccelu zliczenia liczby tych linii.
find . -maxdepth 1 -type dwyjściem?
find $dirs ...(b), jeśli znajdują się wyłącznie w jednym katalogu wyższego poziomu, glob z tego katalogu,find */ ...
-exec echodo swojej komendy find - w ten sposób nie odbija echa nazwy pliku, tylko nowy wiersz.
Czy przez „bez rekurencji” rozumiesz, że jeśli directoryName1ma 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 -ftest 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ć -fsię -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 %qpolega 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'.
findPolecenie 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, xargsi 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
findkiedy 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..)