Расширение переменных внутри одинарных кавычек в команде в Bash

394

Я хочу запустить команду из скрипта bash, который содержит одинарные кавычки и некоторые другие команды внутри одинарных кавычек и переменную.

например repo forall -c '....$variable'

В этом формате $экранируется, а переменная не раскрывается.

Я попробовал следующие варианты, но они были отклонены:

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

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

Пожалуйста, скажите мне, где я иду не так.

Rachit
источник
33
repo forall -c ' ...before... '"$variable"' ...after...'
нет. местоимения м.
2
@nm одинарные кавычки являются частью команды репо. я не думаю, что это будет работать
Rachit
1
bashест одинарные кавычки. Либо вы не в bash, либо одинарные кавычки не являются частью repoкоманды.
нет. местоимения м.
Не уверен, что я вас понял, но в случае необходимости иметь одинарные кавычки, не сработает ли repo forall -c "'... before ..." $ variable "... after ...'"?
ECV
Это bash-скрипт, работающий в bash shell и команда repo forall принимает параметры в одинарных кавычках. Если я подставлю фактическое значение, команда будет работать нормально.
Рач

Ответы:

622

Внутри одинарных кавычек все сохранилось буквально без исключения.

Это означает, что вы должны закрыть кавычки, вставить что-то, а затем снова войти.

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

Конкатенация слов просто делается путем сопоставления. Как вы можете убедиться, каждая из приведенных выше строк является одним словом в оболочке. Кавычки (одинарные или двойные, в зависимости от ситуации) не изолируют слова. Они используются только для отключения интерпретации различных специальных символов, таких как пробелы $, ;... Хороший учебник по цитированию см. В ответе Марка Рида. Также актуально: Какие символы нужно экранировать в bash?

Не объединять строки, интерпретируемые оболочкой

Вы должны абсолютно избегать создания команд оболочки путем объединения переменных. Это плохая идея, похожая на конкатенацию фрагментов SQL (инъекция SQL!).

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

Например, следующее очень небезопасно. НЕ ДЕЛАЙТЕ ЭТОГО

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

Если содержимое $myvarне заслуживает доверия, вот эксплойт:

myvar='foo"; echo "you were hacked'

Вместо приведенного выше вызова используйте позиционные аргументы. Следующий вызов лучше - он не пригоден для использования:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

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

Джо Со
источник
@ Jo.SO, это не сработает. потому что команда репо будет принимать только параметры, пока не закроются первые кавычки. Все, что после этого будет проигнорировано для репо и сгенерирует еще одну ошибку.
Rachit
8
@Rachit - оболочка не работает так. "some"thing' like'thisэто все одно слово в оболочке. Кавычки ничего не прекращаются, насколько синтаксический обеспокоен, и нет никакого способа для команды под названием от оболочки , чтобы сказать , что было процитировано как.
Марк Рид
3
Это второе предложение («вы не можете даже избежать одиночных кавычек») превращает одинарные кавычки в черные дыры Оболочки.
@Evert: Не знаю, как сказать это лучше. Я удалил предложение.
Джо Со
@JoSo Это позор: нет, шутка не сработает.
97

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

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

Объяснение следует, если вам интересно.

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

Но когда оболочка создает этот массив строк из командной строки, она интерпретирует некоторые символы специально; это сделано для того, чтобы упростить (возможно, сделать) команды для ввода. Например, пробелы обычно указывают границу между строками в массиве; по этой причине отдельные аргументы иногда называют «словами». Но аргумент, тем не менее, может содержать пробелы; вам просто нужен какой-то способ сказать оболочке, что вы хотите.

Вы можете использовать обратную косую черту перед любым символом (включая пробел или другую обратную косую черту), чтобы указать оболочке обрабатывать этот символ буквально. Но пока вы можете сделать что-то вроде этого:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

... это может стать утомительным. Таким образом, оболочка предлагает альтернативу: кавычки. Они бывают двух основных разновидностей.

Двойные кавычки называются «группирующими кавычками». Они препятствуют расширению подстановочных знаков и псевдонимов, но в основном они включают пробелы в слове. Другие вещи, такие как расширение параметров и команд (виды, обозначенные как a $), все еще происходят. И, конечно, если вы хотите, чтобы буквальные двойные кавычки были в двойных кавычках, вы должны использовать обратную косую черту:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

Одиночные кавычки более драконовские. Все между ними воспринимается буквально, включая обратную косую черту. Нет абсолютно никакого способа получить буквальную одинарную кавычку внутри одинарных кавычек.

К счастью, кавычки в оболочке не являются разделителями слов ; сами по себе они не заканчивают слово. Вы можете входить и выходить из кавычек, в том числе между разными типами кавычек, в пределах одного и того же слова, чтобы получить желаемый результат:

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

Так что это проще - намного меньше обратной косой черты, хотя последовательность «закрывающая одинарная кавычка», «обратная косая черта», буквальная одинарная кавычка, открытая одинарная кавычка требует некоторого привыкания.

Современные оболочки добавили еще один стиль цитирования, не указанный в стандарте POSIX, в котором перед одинарной кавычкой стоит начальный знак доллара. Строки так цитируемые следуют аналогичным соглашениям в строковые литералы в языке программирования ANSI C, и поэтому иногда называют «ANSI строкой» и $'... 'пара «ANSI кавычки». Внутри таких строк приведенный выше совет о том, что обратная косая черта воспринимается буквально, больше не применяется. Вместо этого они снова становятся особенными - вы можете не только включить буквальную одинарную кавычку или обратную косую черту, добавив к ней обратную косую черту, но и оболочка также расширяет экранирование символов ANSI C (например, \nдля новой строки, \tдля табуляции и \xHHдля символа с шестнадцатеричный кодHH). В противном случае, однако, они ведут себя как строки в одинарных кавычках: подстановка параметров или команд не происходит:

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

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

Марк Рид
источник
2
Да. Теперь я понимаю. Работает отлично. Большое спасибо за вашу помощь!
Рахит
6
Этот ответ был потрясающим.
Vinícius Ferrão
1
$'string'Соответствует ли формат POSIX? Кроме того, есть ли название для этого?
Wildcard
2
@Wildcard $'string'- это расширение, отличное от POSIX, которое я видел как «строки ANSI»; Я включил эти факты в ответ. Такие строки работают в большинстве современных Bourne-совместимых оболочек: bash, dash, ksh (как AT & T, так и PD), и zsh поддерживают их.
Марк Рид
3

Вот что сработало для меня -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
Манмохан
источник
2
Я надеюсь, что TBL_HDFS_DIR_PATH не предоставлен пользователем, кстати, это позволит сделать хороший впрыск sql ...
domenukk
1
Помещение QUOTEв отдельную переменную совершенно излишне; одинарные кавычки внутри двойных кавычек являются обычным символом. (Кроме того, не используйте верхний регистр для ваших личных переменных.)
tripleee
2

РЕДАКТИРОВАТЬ: (Согласно комментариям в вопросе :)

Я смотрю на это с тех пор. Мне повезло, что у меня было репо. Тем не менее, мне не ясно, нужно ли вам заключать команды в одинарные кавычки принудительно. Я изучил синтаксис репо и не думаю, что вам это нужно. Вы можете использовать двойные кавычки вокруг вашей команды, а затем использовать любые одинарные и двойные кавычки, которые вам нужны, если вы избегаете двойных.

ЭЦВ
источник
Спасибо Кевину за вашу помощь.
Рахит
2

просто используйте printf

вместо

repo forall -c '....$variable'

используйте printf для замены токена переменной на расширенную переменную.

Например:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
AndrewD
источник
Это сломано; printfна самом деле ничего вам здесь не покупается, и неспособность процитировать командную замену вызывает новые проблемы с цитированием.
tripleee
0

Переменные могут содержать одинарные кавычки.

myvar=\'....$variable\'

repo forall -c $myvar
user3734617
источник
Они могут, но это не правильный способ сделать это - вы все равно должны заключать "$myvar"в двойные кавычки.
трипл
-2

Это работает для вас?

eval repo forall -c '....$variable'
drgnfr
источник
8
Это для вас? Этому вопросу пять лет, и у него уже есть некоторые ответы, даже принятые ...
Нико Хаасе