Розширення змінних всередині одиничних лапок в команді на Bash


393

Я хочу запустити команду з bash-скрипту, який має одинарні лапки та деякі інші команди всередині одиничних лапок та змінної.

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

У цьому форматі $виводиться і змінна не розширюється.

Я спробував такі варіанти, але вони були відхилені:

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

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

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

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

Якщо я замінюю значення замість змінної, команда виконується добре.

Скажіть, будь ласка, де я помиляюся.


33
repo forall -c ' ...before... '"$variable"' ...after...'
н. 'займенники' м.

2
@nm одиничні лапки є частиною команди repo. я не думаю, що це спрацює
Рахіт

1
bashїсть одиничні цитати. Або ви не ввійшли bash, або окремі цитати не є частиною repoкоманди.
н. 'займенники' м.

Не впевнений, що я вас знайшов, але у випадку, якщо окремі цитати повинні бути там, не вдалося б повторно forall -c "'... перед ..." $ змінною "... після ...'"?
ecv

Його bash-скрипт працює в bash shell і команда repo forall приймає параметри всередині однієї лапки. Якщо я підміняю фактичне значення, команда працює добре.
Рахіт

Відповіді:


621

Всередині одинарних цитат все зберігається буквально, без винятку.

Це означає, що вам потрібно закрити лапки, щось вставити, а потім знову ввести.

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

Зв'язування слів відбувається просто шляхом складання. Як ви можете переконатися, кожен із вищезазначених рядків - це одне слово до оболонки. Цитати (одиничні або подвійні лапки, залежно від ситуації) не виділяють слова. Вони використовуються тільки для відключення інтерпретації різних спеціальних символів, як пробіл, $, ;... хороший підручник по цитованості см відповідь Марка Ріда. Також актуально: Які символи потрібно уникнути в башті?

Не з'єднуйте рядки, інтерпретовані оболонкою

Вам слід абсолютно уникати побудови команд оболонок шляхом об'єднання змінних. Це погана ідея, схожа на об'єднання фрагментів 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 це не працюватиме. тому що команда repo прийме лише параметри, поки не закриються перші лапки. Все, що після цього буде ігноровано для репо і створить ще одну помилку.
Рахіт

8
@Rachit - оболонка не працює так. "some"thing' like'thisце все одне слово до оболонки. Лапки нічого не припиняються, наскільки синтаксичний стурбований, і немає ніякого способу для команди під назвою від оболонки , щоб сказати , що було процитовано як.
Марк Рід

3
Це друге речення ("ви навіть не можете уникнути одинарних лапок") робить єдині цитати чорними дірами "Оболонки".

@Evert: Не знаю, як сказати це краще. Я вилучив вирок.
Jo So

@JoSo Це прикро: жодна жарт не виходить.

96

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 всі підтримують їх.
Марк Рід

1
Посилання назад - Які символи потрібно уникати в аргументах командного рядка? на обміні стеками Unix та Linux.
Wildcard

3

Нижче наведено те, що працювало для мене -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

2
Я сподіваюся, що TBL_HDFS_DIR_PATH не наданий користувачем вхідний btw, це дозволить приємно ввести sql ...
domenukk

1
Введення QUOTEв окрему змінну абсолютно зайве; одинарні котирування всередині подвійних лапок є лише регулярним символом. (Також не використовуйте верхній регістр для своїх приватних змінних.)
tripleee

2

EDIT: (Відповідно до коментарів, про які йдеться :)

Я розглядав це з тих пір. Мені пощастило, що я проклав репо. Досі мені незрозуміло, чи потрібно вкладати свої команди між окремими лапками. Я заглянув у синтаксис репо і не думаю, що потрібно. Ви можете використовувати подвійні лапки навколо своєї команди, а потім використовувати будь-які одиничні та подвійні лапки, які вам знадобляться, за умови, що ви уникаєте подвійних.


Дякую Кевіну за допомогу.
Рахіт

2

просто використовуйте printf

замість

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

використовувати printf, щоб замінити маркер змінної на розширену змінну.

Наприклад:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")

Це зламано; printfнасправді нічого тут не купуєш, а якщо цитувати команду subititution викликає нові проблеми котирування.
трійка

0

Змінні можуть містити одиничні лапки.

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

repo forall -c $myvar

Вони можуть, але це не правильний спосіб зробити це - ви все одно повинні ставити "$myvar"подвійні лапки.
трійка

-2

Це працює для вас?

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

8
Це для вас? На це питання п’ять років і вже є відповіді, навіть прийняті ...
Ніко Хааз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.