Перебирать определенные файлы в каталоге, используя Bash find

Shellcheck не любит мой for над find петля в баш.

for f in $(find $src -maxdepth 1 -name '*.md'); do wc -w < "$f" >> $path/tmp.txt; done

Вместо этого предлагается:

1  while IFS= read -r -d '' file
2  do
3      let count++
4      echo "Playing file no. $count"
5      play "$file"
6  done <   <(find mydir -mtime -7 -name '*.mp3' -print0)
7  echo "Played $count files"

Я понимаю большинство из них, но некоторые вещи до сих пор неясны.

В первой строке: что такое '' file?

В шестой строке: что делает пустое пространство в < < (find). Являются ли < перенаправляет, как обычно? Если они есть, что значит перенаправить в do блок?

Может кто-нибудь помочь разобрать это? Это правильный способ перебирать файлы определенного вида в каталоге?

2 ответа

Решение

В первой строке: что такое файл?

В соответствии с help read, тот '' это аргумент -d параметр:

-d delim    continue until the first character of 
            DELIM is read, rather than newline

В шестой строке: что делает пустое пространство в <<(find).

Там есть два отдельных оператора. Есть <стандартный оператор перенаправления ввода / вывода, за которым следует <(...) конструкция, которая является специфической для bash конструкцией, которая выполняет подстановку процесса:

Process Substitution

    Process  substitution  is  supported on systems that
    support named pipes (FIFOs) or the /dev/fd method of naming
    open files.  It takes the form of <(list) or >(list).  The
    process list is run with its  input  or output  connected
    to  a FIFO or some file in /dev/fd...

Так что это отправка вывода find командовать в doпетля.

<Перенаправления, как обычно? Если они есть, что значит перенаправить в блок do?

Перенаправление в цикл означает, что любая команда внутри этого цикла, которая читает из stdin будет читать из перенаправленного источника ввода. Как побочный эффект, все внутри этого цикла выполняется в подоболочке, что имеет значение в отношении области видимости переменных: переменные, установленные внутри цикла, не будут видны вне цикла.

Может кто-нибудь помочь разобрать это? Это правильный способ перебирать файлы определенного вида в каталоге?

Для записи, я бы обычно делал это по трубам find в xargs, хотя, какое решение лучше, зависит в определенной степени от того, что вы пытаетесь сделать. Два примера в вашем вопросе делают совершенно разные вещи, и не ясно, чего вы на самом деле пытаетесь достичь.

Но например:

find $src -maxdepth 1 -name '*.md' -print0 |
  xargs -0 -iDOC wc -w DOC

Это будет работать wc на всех *.md файлы. -print0 в find-0 в xargs) разрешить этой команде правильно обрабатывать имена файлов со встроенными пробелами (например, This is my file.md). Если вы знаете, что у вас их нет, просто сделайте:

find $src -maxdepth 1 -name '*.md' |
  xargs -iDOC wc -w DOC

Как правило, вам нужно использовать find если вы хотите выполнить рекурсивный поиск по дереву каталогов (хотя в современном bash вы можете установить параметр оболочки globstar, как предполагает Shellcheck). Но в этом случае вы указали -maxdepth 1, так что ваш find Команда просто перечисляет файлы, которые соответствуют шаблону "$src"/*.md, В этом случае гораздо проще и надежнее использовать глоб (шаблон):

for f in "$src"/*.md; do
  wc -w < "$f"
done >> "$path"/tmp.txt

(Я также процитировал все расширения переменных для безопасности и переместил перенаправление вывода, чтобы оно применялось ко всему циклу for, что несколько более эффективно.)

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

find "$src" -maxdepth 1 -name '*.md' -exec do wc -w {} + >> "$path"/tmp.txt

Чтобы ответить на ваши конкретные вопросы:

  1. В IFS= read -r -d '' file, '' это аргумент -d вариант. Эта опция используется для указания символа, который разделяет строки для чтения; по умолчанию используется символ перевода строки, чтобы read читает по одной строке за раз. Пустая строка совпадает с указанием символа NUL, что find выводит в конце каждого имени файла, если указать -print0 вариант. (В отличие от -exec, -print0 не является стандартом Posix, поэтому не гарантируется работа с каждым find реализация, но на практике это довольно общедоступно.)

  2. Пространство между < а также <(...) чтобы избежать создания токена <<, что указывало бы здесь на документ. Вместо этого он указывает перенаправление (<) из процесса замены (<(...)).

Другие вопросы по тегам