Я нахожусь в каталоге, в котором у меня есть два текстовых файла:
$ touch test1.txt
$ touch test2.txt
Когда я пытаюсь перечислить файлы (с Bash), используя какой-то шаблон, он работает:
$ ls test?.txt
test1.txt test2.txt
$ ls test{1,2}.txt
test1.txt test2.txt
Однако, когда шаблон создается командой, вложенной в $()
, работает только один из шаблонов:
$ ls $(echo 'test?.txt')
test1.txt test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
Что тут происходит? Почему шаблон {1,2}
не работает?
bash
bash-expansion
Герослав Мирашевский
источник
источник
?
он также заключен в кавычки и расширяется после$(...)
его замены, а расширение скобки - нет.$
-разложений:zsh -o globsubst -c 'a=/e*; b={/b*,/v*}; echo $a; echo $b'
.Ответы:
Это сочетание двух вещей. Во-первых, расширение скобок - это не шаблон, который соответствует именам файлов: это просто текстовая подстановка - см. В чем разница между `a [bc] d` (скобки) и` a {b, c} d` (скобки)? , Во-вторых, когда вы используете результат подстановки команды вне двойных кавычек (
ls $(…)
), происходит только сопоставление с шаблоном (и разбиение слов: оператор «split + glob»), а не полный повторный анализ.С
ls $(echo 'test?.txt')
помощью командыecho 'test?.txt'
выводится строкаtest?.txt
(с последним переводом строки). Подстановка команды приводит к строкеtest?.txt
(без заключительного перевода строки, потому что подстановка команды удаляет завершающие переводы строки). Эта подстановка без кавычек подвергается разделению слов, в результате чего получается список, состоящий из одной строки,test?.txt
поскольку в ней нет пробельных символов (точнее, символов внутри$IFS
). Каждый элемент этого одноэлементного списка затем подвергается условному расширению подстановочного знака, и, поскольку?
в строке есть подстановочный знак, расширение подстановочного знака действительно происходит. Поскольку шаблонtest?.txt
соответствует как минимум одному имени файла, элемент спискаtest?.txt
заменяется списком имен файлов, соответствующих шаблонам, в результате получается двухэлементный список, содержащийtest1.txt
иtest2.txt
. Наконецls
вызывается с двумя аргументамиtest1
иtest2
.С
ls $(echo 'test{1,2}')
помощью командыecho 'test{1,2}'
выводится строкаtest{1,2}
(с последним переводом строки). Подстановка команды приводит к строкеtest{1,2}
. Эта подстановка без кавычек подвергается разделению слов, в результате получается список, состоящий из одной строкиtest{1,2}
. Каждый элемент этого одноэлементного списка затем подвергается условному расширению подстановочного знака, которое ничего не делает (элемент остается как есть), так как в строке нет подстановочного знака. Такls
называется с единственным аргументомtest{1,2}
.Для сравнения вот что происходит
ls $(echo test{1,2})
. Командаecho test{1,2}
выводит строкуtest1 test2
(с последним переводом строки). Подстановка команды приводит к строкеtest1 test2
(без заключительного перевода строки). Эта подстановка без кавычек подвергается разбиению слов, что приводит к двум строкамtest1
иtest2
. Затем, поскольку ни одна из строк не содержит подстановочный знак, они остаются одни, поэтомуls
вызывается с двумя аргументамиtest1
иtest2
.источник
.txt
во втором объяснении.Расширение скобок не произойдет после подстановки команд. Вы можете использовать eval, чтобы вызвать другой раунд расширения:
Это результат:
источник
Эта проблема очень специфична
bash
, и это потому, что они решилиbash
отделить расширение фигурной скобки от расширения имени файла (globbing) и выполнить его в первую очередь, перед всеми другими расширениями.Из
bash
справочной страницы:В вашем примере
bash
ваши фигурные скобки будут видны только после выполнения подстановки команды (the$(echo ...)
), когда будет слишком поздно.Это отличается от всех других оболочек, которые выполняют расширение фигурной скобки непосредственно перед (а некоторые даже как часть) расширением пути (globbing). Это включает, но не ограничивается тем,
csh
где были впервые разработаны скобки-расширения.Последний пример является одинаковым в
csh
,zsh
,ksh93
,mksh
илиfish
.Кроме того, обратите внимание, что расширение скобок как часть globbing также доступно через
glob(3)
библиотечную функцию (по крайней мере, в Linux и всех BSD) и в других независимых реализациях (например, в perl:)perl -le 'print join " ", <test{1,2}.txt>'
.Почему это было сделано по-другому
bash
, вероятно, есть история, но я не смог найти никакого логического объяснения, и я считаю, что все последующие объяснения неубедительны.источник
perl
раньшеcsh
вызывался для расширения глобусов, поэтому неудивительно, что он по-прежнему распознает те же операторы глобинга, что иcsh
Пожалуйста, попробуй:::
ls $ (эхо-тест {1,2} \. txt)
С обратной косой чертой. Это работает сейчас. Также удалите, как говорилось в предыдущем постере, цитаты. Точка предназначена не для сопоставления с образцом, а для буквального обозначения Периода.
источник
{1,2}
ведет себя так, как он? Вопрос не задается: «Как я могу получить команду, использующую{1,2}
поведение, с которым команда?
работает?» Вы отвечаете не на тот вопрос. (2) Обратная косая черта не имеет к этому никакого отношения. Ваша команда работает так, как работает, потому что вы удалили кавычки, которые были в команде в вопросе.Это работает, если вы удалите кавычки
источник
?
).