Почему {1,2} печатается командой в $() не интерполируется?

Con base de datos (Base de datos GNU )4.2.2:

sed -r '
  /^\s*$/ { 
    # blank line
:NEXT
    N # append next line to pattern space - if none, autoprint PS and exit
    s/^\s*$\n^\s*$//g;t NEXT # if 2 blank lines, clear PS and loop to NEXT
  }
  # else, autoprint PS and next/exit
' < $MYFILE
8
06.06.2019, 09:36
5 ответов

Это сочетание двух вещей. Во-первых, раскрытие фигурных скобок — это не шаблон, соответствующий именам файлов :, это чисто текстовая замена — см. В чем разница между `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.

17
27.01.2020, 20:08

Работает, если убрать кавычки

$ ls $(echo test{1,2})
test1  test2
0
27.01.2020, 20:08

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.

Раскрытие скобок не происходит после подстановки команд. Вы можете использовать eval, чтобы вызвать еще один раунд расширения :

.
eval echo $(echo '{1,2}lala')

Результат:

1lala 2lala
10
27.01.2020, 20:08

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

Из справочной страницы bash:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.

В вашем примере bashувидит ваши фигурные скобки только после того, как выполнит подстановку команды (на $(echo...)), когда уже слишком поздно.

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

$ csh -c 'ls `echo "test{1,2}.txt"`'
test1.txt test2.txt
$ ksh -c 'ls $(echo "test{1,2}.txt")'
test1.txt  test2.txt

$ var=nope var1=one var2=two bash -c 'echo $var{1,2}'
one two
$ var=nope var1=one var2=two csh -c 'echo $var{1,2}'
nope1 nope2

Последний пример одинаков в csh, zsh, ksh93, mkshили fish.

Кроме того, обратите внимание, что раскрытие фигурных скобок как часть подстановки также доступно через библиотечную функцию glob(3)(, по крайней мере, в Linux и всех BSD ), а также в других независимых реализациях (напр. в Perl:perl -le 'print join " ", <test{1,2}.txt>').

Почему в bashэто было сделано по-другому, вероятно, за этим стоит история, но FWIW я не смог найти никакого логического объяснения, и я нахожу все постфактум -рационализации неубедительными.

6
27.01.2020, 20:08

Пожалуйста, попробуйте:::

ls $ (эхо-тест{1,2}\.txt)

С обратной косой чертой. Теперь это работает. Также удалите, как сказал предыдущий постер, кавычки. Точка предназначена не для сопоставления с образцом, а здесь ее следует воспринимать буквально как точку.

1
27.01.2020, 20:08

Теги

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