Я хочу написать сценарий оболочки /bin/sh
который будет обрабатывать любые файлы, соответствующие шаблону. Это легко обрабатывать 1 или более подходящих файлов. Тем не менее, я нахожу неудобным обрабатывать случай с 0 соответствующими файлами.
Очевидная конструкция:
#!/bin/sh
for f in *.ext; do
handle "$f"
done
где *.ext
может быть одним или несколькими выражениями, которые оболочка сравнивает с путями к файлам, а handle
- это команда оболочки, которая выполняется правильно, если задан путь к существующему файлу, но не работает, если указывается путь, который не сопоставляется с файлом. (В случае, если это вызывает этот вопрос, это *.flac
и ffmpeg
, но я думаю, что это не имеет значения.)
Если есть совпадающие файлы foo.ext
, bar.ext
, то этот скрипт выполняет
handle "foo.ext"
handle "bar.ext"
как и ожидалось. Однако, если не каких - либо соответствующих файлов, этот сценарий выдает сообщение об ошибке , как,
handle: *.ext: No such file or directory
Я думаю, что понимаю, почему это происходит: на странице руководства bash (которая, как я полагаю, подходит и для /bin/sh
) говорится, что «список слов, следующих in
, расширен, генерируя список элементов.… Если раскрытие элементов, следующих за результатами, приводит к пустому списку, команды не выполняются, а возвращаемый статус равен 0. «Очевидно, когда *.ext
" развернут ", список результатов включает *.ext
вместо пустой список Но это не объясняет, как это остановить.
(Update: Существует sh (1)
человек страницы из проекта Фамильная черта, которая описывает sh
Bourne Shell, а не позже Bourne Again Shell bash
В разделе « Генерация имени файла» четко сказано: «Если не найдено имя файла, которое соответствует шаблону, то слово остается без изменений». Это объясняет, почему sh
оставляет шаблон *.ext
в списке.)
Что такое компактный идиоматичный способ написать этот цикл, чтобы он запускался ноль раз без ошибок, если нет подходящих файлов, но будет запускаться один раз для каждого соответствующего файла? Еще лучше то, что будет работать с несколькими шаблонами, такими как:
for f in *.ext1 special.ext1 *.ext2; do ...
Я предпочитаю использовать синтаксис, совместимый с /bin/sh
, для переносимости. Я использую Mac OS X 10.11.6, но я надеюсь на синтаксис, который работает на любой Unix-подобной ОС.
Я придумал пару неуклюжих, не идиоматических способов написать такой цикл. Если я не сразу получу хорошие ответы, я внесу их в протокол как ответы.