Проблемы с одинарными кавычками в скрипте bash
Я пытаюсь запустить команду из скрипта bash. Сама команда содержит одинарные кавычки, но я не могу заставить их работать, так как внутри одинарных кавычек есть переменные, которые необходимо расширить. Вот скрипт bash:
#!/bin/bash
if [ "$2" == "PracSci" ];
then
echo "Running tabulate.science.r"
R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.science.r /dev/tty
else
echo "Running tabulate.r"
R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.r /dev/tty
fi
Когда беги как ./script.sh 2 Vocab May12
например, сама команда выполняется без значений переменных.
Как я могу раскрыть переменные, продолжая выполнять команду?
Просто чтобы прояснить, команда, которую я пытаюсь выполнить:R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.r /dev/tty
(включая одинарные кавычки в команде).
Спасибо!
4 ответа
Краткий ответ: используйте двойные кавычки вместо одинарных (как предложено @Pavel). А когда сомневаешься, пользуйся set -x
чтобы узнать, как оболочка анализирует ваши команды.
Длинный ответ: кавычки не передаются команде; вместо этого они анализируются (и удаляются) оболочкой перед передачей аргументов в команду. Что они делают, это изменяют способ, которым оболочка анализирует то, что находится внутри кавычек (как правило, подавляя специальные значения некоторых символов), что означает, что вам нужно выбрать соответствующие кавычки, чтобы разрешить анализировать специальные символы, которые вы хотите проанализировать, и подавить те вы хотите, чтобы к вам относились как к простым персонажам Например, рассмотрим следующую команду:
R CMD BATCH --no-save --no-restore --slave '--args 2 Vocab May12' tabulate.r /dev/tty
Оболочка будет игнорировать специальные значения всех символов в разделе команды, заключенном в одинарные кавычки. Единственными символами, имеющими какое-либо специальное значение, являются пробелы, которые обычно действуют как разделители между аргументами; в этом случае одинарные кавычки заставляют оболочку обрабатывать их как часть одного аргумента. Эти другие команды:
R CMD BATCH --no-save --no-restore --slave "--args 2 Vocab May12" tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args' '2' 'Vocab' 'May12 tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args\ 2\ Vocab\ May12 tabulate.r /dev/tty
... все делают одно и то же, потому что все они (так или иначе) получают оболочку для обработки пробелов как части аргумента, а не разделителей между аргументами.
Теперь давайте посмотрим на команду, которая не работает для вас:
R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.r /dev/tty
Проблема в том, что хотя одиночные кавычки подавляют особое значение пробелов (как вы хотите), они также подавляют особое значение $
с (что вы не хотите). Так что вам нужно что-то более избирательное. Одним из вариантов будет процитировать / убежать пробелы, но оставить $1
и т.д. без кавычек:
R CMD BATCH --no-save --no-restore --slave --args' '$1' '$2' '$3 tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args\ $1\ $2\ $3 tabulate.r /dev/tty
(обратите внимание, что обе эти команды выполняют одно и то же.) Они в основном работают, но могут привести к проблемам: оболочка заменит $1
и т.д. с аргументами сценария, но затем он выполняет их дополнительный анализ: ищет пробелы для использования в качестве разделителей аргументов, подстановочные знаки для сопоставления имен файлов и т. д. Все, что я почти уверен, что вы не хотите - хотя, поскольку аргументы, вероятно, не будут содержать никаких специальных символов, это, вероятно, не будет проблемой. Наверное.
Лучший вариант, который я вижу, это просто использовать двойные кавычки:
R CMD BATCH --no-save --no-restore --slave "--args $1 $2 $3" tabulate.r /dev/tty
Двойные кавычки подавляют особое значение пробелов (как вы хотите), позволяют $
чтобы инициировать расширение переменных (также, как вы хотите), НО не допускайте дальнейшего разбора после раскрытия переменных (также, вероятно, того, что вы хотите). Пробелы или другие забавные символы в аргументах могут по-прежнему вызывать проблемы в сценарии R, но по крайней мере это предотвращает возникновение проблем даже до запуска сценария R.
Вы можете использовать "(двойные кавычки) вместо или закрывая" с "т.е.
#!/bin/bash
if [ "$2" == "PracSci" ];
then
echo "Running tabulate.science.r"
R CMD BATCH --no-save --no-restore --slave '--args '$1' '$2' '$3 tabulate.science.r /dev/tty
else
echo "Running tabulate.r"
R CMD BATCH --no-save --no-restore --slave '--args '$1' '$2' '$3 tabulate.r /dev/tty
fi
Или если вам нужны аргументы... снова в одной цитате, то просто напишите
#!/bin/bash
if [ "$2" == "PracSci" ];
then
echo "Running tabulate.science.r"
R CMD BATCH --no-save --no-restore --slave "'--args $1 $2 $3'" tabulate.science.r /dev/tty
else
echo "Running tabulate.r"
R CMD BATCH --no-save --no-restore --slave "'--args $1 $2 $3'" tabulate.r /dev/tty
fi
Если я вас правильно понимаю, вы хотите " '--args $1 $2 $3' "
вместо простого '--args $1 $2 $3'
, Поскольку токен верхнего уровня является строкой в двойных кавычках, переменные будут расширены, а '
останется в токене также, чтобы R мог их использовать.