/bin/sh: различие между переменной и прямой командой

Предположите, что у меня есть следующий сценарий удара:

#/bin/sh
#next line will work correctly, outputting user name and current directory
start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'

MYVAR="start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'"
#next line will fail with following error:
#ls': -c: line 0: unexpected EOF while looking for matching `''
$MYVAR

Вопрос - почему второй подход через переменную не работает? Как заставить его работать?

3
задан 13 July 2014 в 17:15
2 ответа

Как объяснял @lled, проблема в том, что оболочка обрабатывает котировки перед расширением переменных, поэтому помещение котировок в переменные не делает ничего полезного. Но есть пара альтернатив, в зависимости от того, почему вы хотите сохранить команду (а не просто выполнять ее напрямую).

Если вы хотите определить команду только один раз, то используйте ее повторно, используйте функцию:

myfunc() {
    start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'
}
# ...
myfunc

Если вам нужно собрать/выбрать команду в одном месте, то используйте ее в другом месте, вы можете использовать массив bash для ее хранения. Храните каждое "слово" команды как элемент массива, и если вы правильно на него ссылаетесь, то эти слова будут сохранены:

myarray=(start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls')
# ...
"${myarray[@]}"

Здесь происходит то, что массив определяется как имеющий элементы "start-stop-daemon", "--start", "--exec", "/bin/su", "--", "root", "-c" и "whoami; ls". Одиночные кавычки не хранятся как часть массива, но имеют эффект, делающий "whoami; ls" единым элементом массива. Затем, при расширении массива, [@] сообщает оболочке, что каждый элемент массива должен быть развернут в отдельное слово, а двойные кавычки вокруг него предотвращают какое-либо дополнительное разбиение слова на результирующие значения.

Дополнительную информацию (и еще пару опций) смотрите в BashFAQ #50:Я пытаюсь поместить команду в переменную, но в сложных случаях всегда происходит сбой!

2
ответ дан 3 December 2019 в 06:06

Bourne (и снова Борн) shell имеет сложные правила от разбора строки до выполнения команды.

Для упрощения, давайте сократим вашу прямую командную строку до

su -c 'whoami; ls'

, а ваши "встроенные" - до

MYVAR="su -c 'whoami; ls'"     
$MYVAR                   

В первом случае командная строка разбита на 3 лексемы (слова), потому что символ пробела является метахарактерным разделительным словом, а также потому что кавычки экранируют метахарактерные символы (таким образом, экранируется пробел в кавычки). Перед выполнением команды применяется этап удаления кавычек, оставляя 3 слова и без кавычек. Первым словом является имя команды su, а двумя другими - -c и whoami; ls, параметры команды. Правильно.

Во втором случае вызов состоит из одного расширения параметра. Для некоторых расширений (в том числе и для расширения параметра) применяется разделение слов. Для этой цели используется специальная переменная с именем IFS, которая вычисляет слова из значения расширенного параметра. По умолчанию IFS состоит из пробела, символов табуляции и новой строки. Это означает, что каждый из этих символов используется для разбиения букв на слова. Здесь расширенное значение равно:

su -c 'whoami; ls'

и в итоге мы получаем 4 слова, а именно: su, -c, 'whoami; и ls'. Кроме того, этап удаления кавычек не происходит для расширенных значений параметров. Выдается команда (с именем команды su и ее 3 аргументами), и вы получаете странное сообщение об ошибке.

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

Что можно сделать, так это поиграть с переменной IFS. Одним из решений будет:

IFS=: MYVAR="su:-c:whoami; ls"
$MYVAR

IFS здесь переопределена, чтобы исключить символ пробела из разделения слов, задача принимается за вновь введенный символ двоеточия.

.
2
ответ дан 3 December 2019 в 06:06

Теги

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