Поведение подстановки команды bash командой из строки в переменной

Использование bash и cat (не на месте):

cat <(echo '#!/bin/sh') foo.sh

Или на месте с использованием GNU awk> = 4.1:

awk -i inplace 'BEGINFILE{print "#!/bin/sh"}{print}' foo.sh

2
04.10.2017, 23:38
2 ответа

Почитав немного, попробую ответить сам

Почему так себя ведет подстановка команд?

$ a='echo x; echo y'
$ echo $($a)  # expect 'x y'

Подстановка команд

Заметим, что подстановка переменной $aвыполняется в подоболочке, созданной во время подстановки команд, а не в текущей оболочке ( 1, 2, 3). Следовательно, подоболочка выполняет команду $a, а не echo x; echo y(значение переменной aнаследуется)

Итак, теперь нам нужно только выяснить почему оболочка ведет себя так:

$ a='echo x; echo y'
$ $a
x; echo y

Разбиение слов

Согласно Справочному руководству Bash:

Существует семь видов расширения … Порядок раскрытия следующий: раскрытие фигурной скобки; расширение с помощью тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разбиение слов; и расширение имени файла

— из раздела 3.5 Расширения оболочки


Оболочка сканирует результаты расширения параметров, подстановки команд и арифметического расширения, которые не встречались в двойных кавычках для разделения слов .

Оболочка обрабатывает каждый символ $IFSкак разделитель и разбивает результаты других расширений на слова, используя эти символы в качестве разделителей полей

— из раздела 3.5.7 Разделение слов

(Значение по умолчанию для IFSравно )


Переменная IFS используется для разделения только результаты раскрытия, а не все слова (см. Разделение слов)

— из Приложение B. Основные отличия от оболочки Bourne


метасимвол

Символ, который без кавычек разделяет слова . Метасимвол — это пробел, табуляция, перевод строки или один из следующих символов: |, &, ;, (, ), <или >.

— из раздела 2. Определения


Краткое описание работы оболочки при чтении и выполнении команды. По сути, оболочка делает следующее:

  1. Читает свой ввод.
  2. Разбивает ввод на слова и операторы, соблюдая правила цитирования. Эти токены разделены метасимволами.
  3. Разбирает токенына простые и составные команды.
  4. Выполняет различные расширения оболочки.
  5. Выполняет все необходимые перенаправления.
  6. Выполняет команду

— из секции 3.1.1 Операция оболочки

Теперь мы видим, что на самом деле существует два разных вида «разбиения слов»— начальное разбиение слов (шаг 2) и разделение слов, которое является своего рода расширением оболочки (шаг 4).

Начальное разбиение слов (шаг 2) обрабатывает метасимволы, такие как ;, (, )как разделители; его результат анализируется(шаг 3), поэтому метасимволы и ключевые слова распознаются.

Затем bash выполняет различные расширения оболочки (шаг 4) и, среди прочего, «разбиение на слова» — разновидность их. Этот тип разбиения слов рассматривает только символы IFSкак разделители. Он не заботится о метасимволах. Его результат не анализируетсяповторно, как и результат любого другого расширения. Таким образом, метасимволы и ключевые слова не распознаются.

Вот почему ;внутри значения переменной aне рассматривается как разделитель команд. $aстановится 'echo' 'x;' 'эхо' 'у'. Даже если значение aравно 'echo x ; echo y'слово ';'не будет рассматриваться как метасимвол, поэтому команда $aстанет 'echo' 'x' ';' 'эхо' 'у'.

Резюме:

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

$ 'echo' 'a' ';' 'echo' 'b'  # ';' is a literal
a ; echo b

$ cmd=echo
$ "$cmd" a ; $(printf echo) b  # ';' is a metacharacter
a
b

$ cmd='echo a'  # simple command is executed properly
$ $cmd
a

$ cmd='echo a;'  # but metacharacters are not recognized
$ $cmd echo b
a; echo b

$ echo a $(echo ';') echo b  # ';' is a literal
a ; echo b

$ $(printf if) true; then echo a; fi  # parsing error
bash: syntax error near unexpected token 'then'

$ $(printf if) true; $(printf then) echo a; $(printf fi)  # looking for command "if"
bash: if: command not found
bash: then: command not found
bash: fi: command not found

Как выполнить подстановку commandдля списка команд, хранящихся в переменной, без использования eval и bash -c (поскольку eval — это зло)?

Вероятно, правильный ответ "это тоже зло", так что вам не нужно избегать eval :)

Переменные содержат данные. Функции содержат код. Не помещайте код в переменные!..

— из статьи Пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!

Ссылки

Обмен стеками Unix:

Подстановка команд оболочки Bash

Почему переменная отображается в подоболочке?

Действительно ли скобки помещают команду в подоболочку?

Другое:

Справочное руководство по Bash

Я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!

диалог в системе отслеживания ошибок Debian

5
27.01.2020, 21:53

Разбиение на слова происходит довольно поздно при оценке команды. Что наиболее важно для вас, это происходит после раскрытия переменных и подстановки команд.

Это означает, что вторая строка в

s="echo a; echo b"
echo $($s)

сначала расширит $sдо echo a; echo b, а затем эта команда будет выполнена без разделения на составную команду, состоящую из двух echos.

(details:$sразбивается на четыре слова: echo, a;, echoи b. Инкапсулирующий $(...)выполняет это как одну команду, echoс тремя аргументами, a;, echoи b.)

То, что дается самому внешнему echo, является строкойa; echo b(на самом деле из трех слов, поскольку $(...)не заключено в кавычки ), так как это было то, что было выведено самым внутренним echo.

Сравните это с

s1="echo a"
s2="echo b"
echo $($s1;$s2)

что приводит к ожидаемому результату.

Да, "evalзло", большую часть времени,и sh -cнеуклюжий и такой же хрупкий. Но если у вас есть фрагмент кода оболочки, которому вы доверяете в строке в сценарии оболочки, то эти инструменты являются единственными (? )способ заставить этот код выполняться должным образом, так как это часто требует явной оценки текста в строке как кода оболочки (со всеми фазами оценки от начала до конца ). Особенно, если это составная команда.


Я думаю, что только из-за тесной связи оболочки Unix с текстом вам посчастливилось echo $($s)выполнить что-то вообще .

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

6
27.01.2020, 21:53

Теги

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