Ошибка в скрипте. Команда find пропускает папки с пробелом

Ошибка в скрипте. Команда find пропускает папки с пробелом

Мне нужен скрипт, который подсчитывает файлы в каталоге (и подкаталогах). Я взял следующий скрипт и изменил его под свои нужды.

Работает как надо, за исключением папок с пробелами. Я почти уверен, что где-то не хватает кавычек, но пока не могу разобраться.

Дополнительная информация

  • Linux 2.6.22.19-0.4-default (этот сервер больше не находится в продуктивной среде.)
  • GNU find версия 4.2.31
  • Я не могу переименовать каталоги.

Пример структуры каталога

.
..
01_infos
02_sent
03_inbox
04_public and private
197.
145.
329.
13.

Сценарий

#!/bin/bash
# Write a script that will count the number of files in each of your subdirectories.
# -------------------------------------------------------------------------
# Copyright (c) 2001 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# -------------------------------------------------------------------------

START=$HOME

# change your directory to command line if passed
# otherwise use home directory
[ $# -eq 1 ] && START=$1 || :

if [ ! -d $START ]
then
        echo "$START not a directory!"
        exit 1
fi

# use find command to get all subdirs name in DIRS variable
DIRS=$(find "$START" -type d)

# loop thought each dir to get the number of files in each of subdir
for d in $DIRS
do
        echo "$d directory has $(find "$d" -maxdepth 1 -regex '.*\.' -type f | wc -l) files" || :
done

выход

./01_infos directory has 1 files
./02_sent directory has 9 files
./03_inbox has 4 files
find: ./04_public: No such file or directory

решение1

У вас не хватает двойных кавычек (всегда заключайте подстановки переменных $fooи подстановки команд в двойные кавычки$(foo), если только вы не знаете, почему вы можете безопасно их отключить, а почему вам нужно их отключить). Но это еще не вся проблема.

if [ ! -d $START ]

должно быть if [ ! -d "$START" ].

DIRS=$(find "$START" -type d)

На этом этапе DIRSсодержит имя начального каталога и его подкаталогов рекурсивно, с символами новой строки между ними. Так что если у вас есть имя каталога, содержащее символ новой строки, вы проиграли: невозможно узнать, какие символы новой строки пришли из имени каталога, а какие были разделителями. Если вы знаете, что в именах файлов нет символов новой строки, вы можете проанализировать вывод find, но как вы узнаете?

Кстати, здесь можно не использовать двойные кавычки, $(…)потому что это присваивание переменных, а подстановки в присваиваниях неявно защищены. Однако, обратите внимание, что export DIRS=$(…)не защищено подобным образом. Лучше использовать кавычки, если вы не владеете языком скриптов оболочки, как и все люди, которые будут поддерживать ваш скрипт.

for d in $DIRS

Вот где вы проигрываете: вы хотите разбить $DIRSна слова, поэтому вы не можете поставить двойные кавычки, но они вам нужны, потому что $DIRSвсе элементы будут объединены вместе, а имена файлов внутри пробелов будут разделителями, если вы оставите их без кавычек.


Обычно, когда вы используете find, вы должны заставить его вызвать команду обработки с -execопцией. Если у вас нет жесткого контроля над именами файлов, не анализируйте вывод find: он неоднозначен.

find "$START" -type d -exec sh -c '
    echo "$0 directory has $(find "$0" -maxdepth 1 -regex ".*\\." -type f -printf \\n | wc -l) files whose name ends with ."
' {} \;

Обратите внимание еще раз на то, что во встроенной findкоманде при анализе выходных данных findваш подсчет будет неверным, если какое-либо имя файла содержит символ новой строки.

решение2

А как насчет этого?

find . -type d -exec sh -c '/bin/echo -n "{}"; find "{}" -maxdepth 1 -regex ".*\." -type f | wc -l; ' \;

Вывод не такой приторный, но он не требует скрипта и работает для каталогов с пробелами, а также другими небуквенно-цифровыми символами.

решение3

У вас классическая ошибка кавычек. Исправьте цикл for так, чтобы он выглядел так:

for d in "$DIRS"

В качестве альтернативы вы можете findнапрямую передать его вывод, например:

find "$START" -type d | while read d
do # and so on...

Кстати, этот || :бит совершенно избыточен, поскольку echoвсегда имеет возвращаемое значение 0.

Связанный контент