Использование кавычек при подстановке команд

#bash #shell

#bash #оболочка

Вопрос:

Я не понимаю, почему это работает

 dateCMD=$(date -d "4 hours ago")
echo $dateCMD
Tue Oct 18 02:20:34 AEDT 2016
  

и это работает

 dateCMD="date"
$dateCMD
Tue Oct 18 06:23:19 AEDT 2016
  

Но не это

 dateCMD='date -d "4 hours ago"'
$($dateCMD)
date: extra operand ‘ago"’
  

Как я могу заставить этот последний случай работать?

Комментарии:

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

Ответ №1:

Короткий ответ: вы не хотите этого делать. Вместо этого используйте функцию.

 dateCMD () {
  date -d "4 hours ago"
}

dateCMD
  

Длинный ответ: кавычки в значении параметра не являются «синтаксическими» кавычками; это просто обычные данные. Когда вы пишете

 dateCMD='date -d "4 hours ago"'
$($dateCMD)
  

Он оценивается следующим образом:

  1. $dateCMD расширяется до date -d "4 hours ago"
  2. Расширение разбито на слова date , -d , "4 , hours , и ago" . Кавычки не обрабатываются специально.
  3. Первое слово, date , обрабатывается как команда, а остальные слова передаются как отдельные аргументы.
  4. date используется "4 в качестве аргумента для -d параметра. Поскольку date принимает только один дополнительный позиционный аргумент (строку формата), он принимает hours в качестве этого аргумента, а затем жалуется, что ago это дополнительный операнд.

Комментарии:

1. Хорошее объяснение, почему это не работает, спасибо. Я также понял eval , что это сработает (с очевидными оговорками безопасности), и ответ anubhava на array был интересным.

Ответ №2:

Используйте массив BASH, чтобы заставить его работать:

 dateCMD=(date -d "4 hours ago")
"${dateCMD[@]}"
Mon Oct 17 11:38:54 EDT 2016
  

Я печатал объяснение вашей проблемы с цитированием, но затем я заметил очень хорошо объясненный ответ от @chepner, поэтому избегал добавления избыточной информации в мой ответ.

Комментарии:

1. Интересное решение для bash 4. Спасибо. Я также обнаружил eval , что это сработает.

2. Это должно работать не только с BASH 4, но и с BASH 3.2.

3. Вы правы. Я не понимал, что массивы также можно использовать в Bash 3.2, спасибо.

Ответ №3:

Чтобы заставить последний случай работать, вы можете использовать eval

Конечно, вы не должны использовать eval для любого пользовательского ввода без полной проверки этого ввода.

 dateCMD='date -d "4 hours ago"'
eval $dateCMD