Использование bash
и cat
(не на месте):
cat <(echo '#!/bin/sh') foo.sh
Или на месте с использованием GNU awk> = 4.1:
awk -i inplace 'BEGINFILE{print "#!/bin/sh"}{print}' foo.sh
Почитав немного, попробую ответить сам
$ 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. Определения
Краткое описание работы оболочки при чтении и выполнении команды. По сути, оболочка делает следующее:
- Читает свой ввод.
- Разбивает ввод на слова и операторы, соблюдая правила цитирования. Эти токены разделены метасимволами.
- Разбирает токенына простые и составные команды.
- Выполняет различные расширения оболочки.
- Выполняет все необходимые перенаправления.
- Выполняет команду
— из секции 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
Вероятно, правильный ответ "это тоже зло", так что вам не нужно избегать eval :)
Переменные содержат данные. Функции содержат код. Не помещайте код в переменные!..
— из статьи Пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!
Подстановка команд оболочки Bash
Почему переменная отображается в подоболочке?
Действительно ли скобки помещают команду в подоболочку?
Справочное руководство по Bash
Я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!
Разбиение на слова происходит довольно поздно при оценке команды. Что наиболее важно для вас, это происходит после раскрытия переменных и подстановки команд.
Это означает, что вторая строка в
s="echo a; echo b"
echo $($s)
сначала расширит $s
до echo a; echo b
, а затем эта команда будет выполнена без разделения на составную команду, состоящую из двух echo
s.
(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, который ей передается в виде строки...