Почему $() не расширяется должным образом?

Возможно, самым простым способом было бы скопировать двоичный файл lsиз какой-либо другой системы — например, по сети или на флэш-накопителе USB — и поместил в свой личный binкаталог. Другой обходной путь — использовать statс подстановочными знаками.

-1
25.08.2019, 10:39
2 ответа

$()расширяется должным образом, но оболочка делает больше для результата, чем вы знаете. Кроме того, добавление буквенных кавычек к путевым именам почти никогда не бывает правильным (, если только вы не используете evalгде-то ).

Воспроизведение всех файлов MP3 в диапазоне ~/Documents/musicили ниже с помощьюvlc:

find ~/Documents/music -type f -name '*.mp3' -exec vlc {} +

Это запустит vlcкак можно больше файлов MP3 одновременно.

Чтобы пропустить использование findвсех -вместе:

shopt -s globstar
vlc ~/Documents/music/**/*.mp3

Это будет работать до тех пор, пока у вас не будет большого количества файлов MP3 (, и в этом случае вы получите сообщение об ошибке, поскольку список аргументов станет слишком длинным ).

Универсальный объект **"рекурсивно" соответствует файловой иерархии, но должен быть включен с помощью shopt -s globstarвbash(оболочке zshэтот глобус включен по умолчанию ).


Ваша команда

vlc $(echo $(find ~/Documents/music -name "*.mp3" -exec echo "\"{}\" \n " \;) | head -n 1)

сначала выполнит find,headи echo. Этот конвейер будет выводить

"/home/XXX/Documents/music/My Folder/1 - track/the name of track.mp3"

Затем оболочка разбивает эту строку на слова "/home/XXX/Documents/music/My, Folder/1, -, track/the, name, ofи track.mp3"по пробелам в строке.

Затем

vlcбудет вызываться с этими отдельными аргументами. В частности, аргумент -может быть ошибочно принят за параметр, в то время как другие, скорее всего, будут восприниматься как отдельные операнды имени пути.

Это подробно объясняется в

2
28.04.2021, 23:30

Котировки не обрабатываются при выводе расширений. Если вы сделаете $(echo '"foo bar"')или $(echo "\"foo bar\""), аргументом основной команды станет "fooи bar". С буквальными кавычками и как два отдельных аргумента из-за разделения слов .

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

Что вы можете сделать, так это:

find... -print0 | xargs -0 vlc 

или

find... -exec vlc {} +

Первый выводит findимена файлов, разделенные нулевыми байтами, и указывает xargsожидать именно этого. Однако не все системы имеют find -print0и xargs -0.

Второй имеет findзапуск самого себя vlc, завершающий {} +сообщает ему передать несколько имен файлов для одного вызова vlc.

Если нужно найти очень большое количество файлов, и они не помещаются в одну командную строку, то оба они могут запустить vlcболее одного раза.

См.:

Использование echoдля отладки этого на самом деле не работает, так как он будет объединять свои аргументы с одним пробелом, делая невозможным определение разницы между echo foo barи echo "foo bar".

4
28.04.2021, 23:30

Теги

Похожие вопросы