Ситуация такова, у меня есть MP3-плеер, mpg321
который принимает список файлов в качестве аргумента. Я храню свою музыку в каталоге под названием «музыка», в котором есть еще несколько каталогов. Я просто хочу сыграть все из них, поэтому я запускаю программу с
mpg321 $(find /music -iname "*\.mp3")
, Проблема в том, что в некоторых именах файлов есть пробелы, и программа разбивает эти имена на более мелкие части и жалуется на отсутствующие файлы. Завершение результата find
в кавычки
mpg321 "$(find /music -iname "*\.mp3")"
не помогает, потому что все станет одним большим «именем файла», которое явно не найдено.
Как я могу это сделать тогда? Если это имеет значение, я использую bash
, но скоро переключусь на zsh
.
источник
mpg321 $(find /music -iname "*\.mp3" -print0)
нет?mpg321
имеет к этому никакого отношения, это оболочка, которая разбивает выводfind
на отдельные аргументы. И-print0 | xargs -0
будет работать с каждым возможным именем файла.С GNU find вы также можете использовать
-print0
иxargs -0
, но нет смысла изучать еще один инструмент.-exec ... {} +
Синтаксис получает мало упоминания , потому что Linux приобрел позже , чем-print0
, но нет никаких причин , чтобы не использовать его сейчас.С zsh или bash 4 это намного проще:
Только в zsh вы можете сделать (часть a) шаблон без учета регистра:
источник
Я думаю, что решение Стивена лучше, но другой способ - использовать
-I
флаг xargs , который позволяет вам указать строку, которая затем будет заменена в команде аргументом (вместо простого добавления аргумента в конец команды). Вы можете использовать это, чтобы процитировать аргумент:источник
-0
флаг Xargs без Find-print0
. Кроме того,-I
флаг Xargs имеет ряд последствий. В отличие от простого,xargs
который будет сжимать все строки из stdin в одну команду, онxargs -I
будет выполнятьсяmpg321
потенциально сотни или тысячи раз (по одному разу для каждого файла), что, конечно, не является целью. Также имейте в виду, что указывать foo не нужно, так какxargs
это делается внутри. Обратите внимание, что если вы сожмете все имена файлов в строку и отправите ихxargs -I
, они не откроются.Обычно лучше напрямую,
-exec ${tgt_process} \{\} +
но если по какой-либо причине вам необходимо получить надежно разграниченный список имен файлов в файле или потокеfind
, то вы можете сделать это:Из этого вы получаете две уникальные строки. В начале каждого имени файла находится строка,
\n///
а в конце каждого имени файла - строка///\n
. Эти две строки не встречаются нигде вfind
выходных данных, кроме как в тех позициях, независимо от того, какие символы содержатся в именах файлов.Кроме того, вышеприведенное использование является базовым переносимым POSIX и может использоваться практически для любой системы Unix. Это не относится к использованию нулевого байтового разделителя - несмотря на его удобство - рекомендованного некоторыми другими.
Но, опять же, это необходимо только в том случае, если вы не можете напрямую
-exec
обратиться$tgt_process
к вам по какой-либо причине, поскольку это должно быть вашей целью. С одной стороны, вышеупомянутый метод все еще требует парсинга. Например, если вы хотите, чтобы каждая оболочка имени файла заключалась в кавычки, вы должны сначала убедиться, что любые жесткие кавычки в имени файла были экранированы:Это выводит должным образом экранированный массив имен файлов независимо от того, какими могут быть их составные символы. Теперь вам остается только надеяться, что ваше приложение на принимающей стороне не будет портить его.
источник
Еще один способ сделать это - экранировать все специальные символы в именах файлов. Например:
Это в основном передаст правильно экранированные имена файлов в xargs для выполнения, и никаких проблем не возникнет.
источник
sed 's|.|\\&|g'
- это то, что POSIX рекомендует в любом случае.