Мені потрібно перевірити всі підкаталоги та повідомити, скільки файлів (без подальшої рекурсії) вони містять:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Мені потрібно перевірити всі підкаталоги та повідомити, скільки файлів (без подальшої рекурсії) вони містять:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Відповіді:
Це робить це безпечним і портативним способом. Це не заплутається дивними іменами.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Зверніть увагу, що спочатку буде надруковано кількість файлів, а потім ім'я каталогу в окремому рядку. Якщо ви хочете зберегти формат ОП, вам знадобиться подальше форматування, наприклад
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Якщо у вас є певний набір цікавих підкаталогів, ви можете замінити *їх.
Чому це безпечно? (і тому сценарій гідний)
Імена файлів можуть містити будь-який символ, крім /. Є кілька символів, які спеціально обробляються або оболонкою, або командами. До них відносяться пробіли, нові рядки та тире.
Використання for f in *конструкції - це безпечний спосіб отримання кожного імені файлу, незалежно від того, що він містить.
Коли у вас є ім'я файлу в змінній, вам все одно доведеться уникати таких речей find $f. Якщо воно $fмістило б ім'я файлу -test, findскаржиться на варіант, який ви йому щойно надали. Спосіб уникнути цього - використання ./перед іменем; таким чином він має те саме значення, але він більше не починається з тире.
Нові рядки та пробіли також є проблемою. Якщо $fміститься "привіт, приятель" як ім'я файлу find ./$f, є find ./hello, buddy. Ти кажеш findподивитися ./hello,і buddy. Якщо їх не існує, він скаржиться, і він ніколи не загляне ./hello, buddy. Це легко уникнути - використовуйте лапки навколо змінних.
Нарешті, імена файлів можуть містити нові рядки, тому підрахунок нових рядків у списку імен не буде працювати; ви отримаєте додатковий підрахунок для кожного імені файлу з новим рядком. Щоб уникнути цього, не рахуйте нові рядки у списку файлів; натомість порахуйте нові рядки (або будь-який інший символ), що представляє один файл. Ось чому findкоманда просто -exec echo \;і ні -exec echo {} \;. Я хочу надрукувати лише один новий рядок з метою підбору файлів.
-mindepth 1
-printf '\n'замість -exec echo.
-printf, але не, якщо ви хочете, щоб він працював на FreeBSD, наприклад.
Якщо припустити, що ви шукаєте стандартне рішення для Linux, то це досить просто find:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Там, де findпроходять два зазначені підкаталоги, до -maxdepthчисла 1, що запобігає подальшій рекурсії, і лише повідомляє файли ( -type f), розділені новими рядками. Потім результат передається wcдля підрахунку кількості цих рядків.
find . -maxdepth 1 -type dрезультатом?
find $dirs ...або, (b) якщо вони є виключно в одному каталозі вищого рівня, глобусі з цього каталогу,find */ ...
-exec echoдо команди пошуку - таким чином вона не буде перегукуватися з іменем файлу, а лише з новим рядком.
Під "без рекурсії" ви маєте на увазі, що якщо directoryName1є підкаталоги, то ви не хочете рахувати файли в підкаталогах? Якщо так, ось спосіб підрахунку всіх регулярних файлів у зазначених каталогах:
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
Зауважте, що -fтест виконує дві функції: він перевіряє, чи є запис, узгоджений одним із глобусів вище, звичайним файлом, і перевіряє, чи була відповідність запису (якщо один із глобусів нічого не відповідає, шаблон залишається таким, як є¹). Якщо ви хочете порахувати всі записи в даних каталогах незалежно від їх типу, замініть -fна -e.
У Ksh є можливість зразки збігатися з крапковими файлами та створювати порожній список, якщо жоден файл не відповідає шаблону. Тож у ksh ви можете порахувати звичайні файли на зразок цього:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
або всі файли просто так:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
У Bash є різні способи зробити це простішим. Для підрахунку звичайних файлів:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Для підрахунку всіх файлів:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Як завжди, в zsh це навіть простіше. Для підрахунку звичайних файлів:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Змінити, (DN.)щоб (DN)рахувати всі файли.
¹ Зауважте, що кожен шаблон відповідає самій собі, інакше результати можуть бути вимкнені (наприклад, якщо ви рахуєте файли, які починаються з цифри, ви не можете просто зробити це, for x in [0-9]*; do if [ -f "$x" ]; then …тому що може бути названий файл [0-9]foo).
На основі сценарію підрахунку , відповіді Шона та хитрості Баша, щоб переконатися, що навіть імена файлів з новими рядками друкуються в зручній формі в одному рядку:
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полягає в тому, щоб надрукувати цитовану версію рядка, тобто однорядковий рядок, який ви можете ввести в сценарій Bash, щоб його інтерпретувати як буквальний рядок, що включає (потенційно) нові рядки та інші спеціальні символи. Наприклад, див echo -n $'\tfoo\nbar'проти printf %q $'\tfoo\nbar'.
findКоманда працює, просто друк одного символу для кожного файлу, а потім рахуючи тих , замість підрахунку рядків.
Ось «груба сила» -ish спосіб отримати результат, використовуючи find, echo, ls, wc, xargsі awk.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
findколи Bash зробить це?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): для всіх каталогів підраховуйте кількість записів у цьому каталозі (включаючи приховані точкові файли, виключаючи.та..)