Rozszerzanie zmiennych wewnątrz pojedynczych cudzysłowów w poleceniu w Bash

393

Chcę uruchomić polecenie ze skryptu bash, który zawiera pojedyncze cudzysłowy i niektóre inne polecenia wewnątrz pojedynczych cudzysłowów i zmienną.

na przykład repo forall -c '....$variable'

W tym formacie $jest zastępowany, a zmienna nie jest rozwijana.

Próbowałem następujących odmian, ale zostały one odrzucone:

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

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

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

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

Jeśli podstawię wartość zamiast zmiennej, polecenie zostanie wykonane dobrze.

Powiedz mi, gdzie się mylę.

Rachit
źródło
33
repo forall -c ' ...before... '"$variable"' ...after...'
n. „zaimki” m.
2
@ nm pojedyncze cudzysłowy są częścią polecenia repo. nie sądzę, żeby to zadziałało
Rachit,
1
bashzjada pojedyncze cytaty. Albo cię nie ma bash, albo pojedyncze cudzysłowy nie są częścią repopolecenia.
n. „zaimki” m.
Nie jestem pewien, czy cię dostanę, ale w przypadku, gdyby pojedyncze cytaty musiały tam być, czy repo forall -c „”… przed… „zmienną $”… po… ”nie zadziała?
ekw.
Jest to skrypt bash działający w powłoce bash, a polecenie repo forall przyjmuje parametry w pojedynczych cudzysłowach. Jeśli podstawię rzeczywistą wartość, polecenie działa poprawnie.
Rachit,

Odpowiedzi:

621

Wewnątrz pojedynczych cytatów wszystko jest zachowane dosłownie, bez wyjątku.

Oznacza to, że musisz zamknąć cytaty, wstawić coś, a następnie ponownie wprowadzić.

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

Łączenie słów odbywa się po prostu przez zestawienie. Jak możesz zweryfikować, każdy z powyższych wierszy jest pojedynczym słowem w powłoce. Cytaty (pojedyncze lub podwójne, w zależności od sytuacji) nie izolują słów. Służą one wyłącznie do wyłączania interpretacji różnych znaków specjalnych, takich jak białe znaki $, ;... Aby uzyskać dobry samouczek na temat cytowania, zobacz odpowiedź Marka Reeda. Również istotne: Które postacie muszą być poprzedzone ucieczką?

Nie łącz łańcuchów interpretowanych przez powłokę

Należy bezwzględnie unikać budowania poleceń powłoki poprzez łączenie zmiennych. Jest to zły pomysł podobny do łączenia fragmentów SQL (wstrzyknięcie SQL!).

Zwykle możliwe jest umieszczenie symboli zastępczych w poleceniu i dostarczenie polecenia wraz ze zmiennymi, aby odbiorca mógł je odebrać z listy argumentów wywołania.

Na przykład następujące jest bardzo niebezpieczne. NIE Rób tego

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

Jeśli zawartość $myvarjest niezaufana, oto exploit:

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

Zamiast powyższego wywołania użyj argumentów pozycyjnych. Następujące wywołanie jest lepsze - nie można go wykorzystać:

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

Zwróć uwagę na użycie pojedynczych znaczników w przypisaniu do script, co oznacza, że ​​przyjmuje się dosłownie, bez zmiennej interpretacji lub jakiejkolwiek innej formy interpretacji.

Jo So
źródło
@ Jo.SO, które nie będą działać. ponieważ polecenie repo zajmie tylko parametry, dopóki pierwsze cudzysłowy nie zostaną zamknięte. Wszystko później zostanie zignorowane w przypadku repo i wygeneruje kolejny błąd.
Rachit,
8
@Rachit - powłoka tak nie działa. "some"thing' like'thisto jedno słowo do powłoki. Znaki cudzysłowu nie kończą niczego w zakresie analizowania i nie ma sposobu, aby polecenie wywoływane przez powłokę informowało o tym, co zostało zacytowane.
Mark Reed,
3
To drugie zdanie („nie można nawet uciec przed pojedynczymi cytatami”) sprawia, że ​​pojedyncze cytaty stanowią czarne dziury w Skorupie.
@Evert: Nie wiem, jak to lepiej powiedzieć. Usunąłem zdanie.
Jo So
@JoSo Szkoda: żart nie pada.
96

repoPolecenie nie może obchodzi jakie cytaty robi. Jeśli potrzebujesz rozszerzenia parametrów, użyj podwójnych cudzysłowów. Jeśli to oznacza, że ​​musisz skończyć z odwrotnym ukośnikiem wiele rzeczy, użyj pojedynczych cudzysłowów dla większości z nich, a następnie wyrwij się z nich i przejdź do podwójnych w części, w której potrzebujesz rozszerzenia.

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

Wyjaśnienie następuje, jeśli jesteś zainteresowany.

Po uruchomieniu polecenia z powłoki to polecenie otrzymuje jako argumenty tablicę ciągów zakończonych znakiem null. Ciągi te mogą zawierać absolutnie dowolny znak inny niż zero.

Ale gdy powłoka buduje tablicę ciągów z wiersza poleceń, interpretuje niektóre znaki specjalnie; ma to na celu ułatwienie (a nawet możliwość) wykonywania poleceń. Na przykład spacje zwykle wskazują granicę między łańcuchami w tablicy; z tego powodu poszczególne argumenty są czasami nazywane „słowami”. Niemniej jednak w sporze mogą znajdować się spacje; potrzebujesz tylko sposobu, aby powiedzieć powłoce, że tego chcesz.

Możesz użyć odwrotnego ukośnika przed dowolną postacią (w tym spacją lub innym odwrotnym ukośnikiem), aby powiedzieć powłoce, aby traktowała tę postać dosłownie. Ale chociaż możesz zrobić coś takiego:

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

... może być męczące. Powłoka oferuje więc alternatywę: znaki cudzysłowu. Występują w dwóch głównych odmianach.

Podwójne cudzysłowy nazywane są „grupowaniem cudzysłowów”. Zapobiegają rozszerzaniu symboli wieloznacznych i aliasów, ale głównie służą do umieszczania spacji w słowie. Inne rzeczy, takie jak rozszerzanie parametrów i poleceń (rzeczy sygnalizowane przez a $) nadal występują. I oczywiście, jeśli chcesz literalnego podwójnego cudzysłowu w cudzysłowach, musisz odwrócić ukośnik:

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

Znaki pojedynczego cudzysłowu są bardziej drakońskie. Wszystko między nimi jest brane całkowicie dosłownie, łącznie z odwrotnymi ukośnikami. Nie ma absolutnie żadnego sposobu na uzyskanie dosłownego pojedynczego cytatu w pojedynczych cytatach.

Na szczęście znaki cudzysłowu w powłoce nie są ogranicznikami słów ; same w sobie nie kończą słowa. Możesz wchodzić i wychodzić z cytatów, w tym między różnymi typami cytatów, w ramach tego samego słowa, aby uzyskać pożądany wynik:

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

To jest łatwiejsze - dużo mniej odwrotnych ukośników, chociaż przyzwyczajenie się do sekwencji z zamkniętym cytatem, odwróconego literału-pojedynczego cytatu, otwartego pojedynczego cytatu.

Nowoczesne powłoki dodały inny styl cytowania nieokreślony przez standard POSIX, w którym wiodący pojedynczy cudzysłów jest poprzedzony znakiem dolara. Tak cytowane łańcuchy są zgodne z konwencjami podobnymi do literałów łańcuchowych w języku programowania ANSI C i dlatego są czasami nazywane „ciągami ANSI” i $''parą „cudzysłowów ANSI”. W przypadku takich ciągów powyższa rada dotycząca dosyłania ukośników dosłownie nie ma już zastosowania. Zamiast tego stają się ponownie wyjątkowe - nie tylko można dołączyć dosłowny pojedynczy cudzysłów lub ukośnik odwrotny, przygotowując do niego odwrotny ukośnik, ale powłoka rozszerza także znaki ucieczki znaku ANSI C (jak \ndla nowej linii, \tdla tabu i \xHHdla znaku z kod szesnastkowyHH). W przeciwnym razie zachowują się jak ciągi cudzysłowu: nie następuje podstawienie parametru ani polecenia:

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

Należy zauważyć, że pojedynczy ciąg otrzymany jako argument echopolecenia jest dokładnie taki sam we wszystkich tych przykładach. Po zakończeniu analizy powłoki w wierszu poleceń nie można uruchomić polecenia, aby powiedzieć, co zostało zacytowane. Nawet gdyby chciał.

Mark Reed
źródło
2
Tak. Teraz rozumiem. Działa idealnie. Bardzo dziękuję za Twoją pomoc!
Rachit 12.12.12
6
Ta odpowiedź była niesamowita.
Vinícius Ferrão
1
Czy $'string'format jest zgodny z POSIX? Czy jest też na to nazwa?
Wildcard
2
@Wildcard $'string'jest rozszerzeniem innym niż POSIX, które widziałem jako „ciągi ANSI”; Włączyłem te fakty do odpowiedzi. Takie ciągi działają w większości nowoczesnych powłok kompatybilnych z Bourne: bash, dash, ksh (zarówno AT&T, jak i PD), a wszystkie zsh obsługują je.
Mark Reed
1
Odsyłacz - Jakie znaki należy wstawić w argumentach wiersza poleceń? na giełdzie stosów Unix i Linux.
Wildcard,
3

Poniżej działało dla mnie -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
Manmohan
źródło
2
Mam nadzieję, że TBL_HDFS_DIR_PATH nie jest podany przez użytkownika przy wejściu btw, to pozwoliłoby na niezły zastrzyk sql ...
domenukk
1
Umieszczenie QUOTEoddzielnej zmiennej jest całkowicie zbędne; pojedyncze cudzysłowy wewnątrz podwójnych cudzysłowów to tylko zwykły znak. (Również nie używaj wielkich liter do zmiennych prywatnych.)
tripleee
2

EDYCJA: (Zgodnie z pytaniami :)

Od tego czasu przyglądam się temu. Miałem szczęście, że miałem repozytorium. Nadal nie jest dla mnie jasne, czy trzeba przymusowo umieszczać polecenia między pojedynczymi cudzysłowami. Zajrzałem do składni repo i nie sądzę, żebyś musiał. Możesz użyć podwójnych cudzysłowów wokół swojego polecenia, a następnie użyć dowolnych pojedynczych i podwójnych cudzysłowów, które potrzebujesz w środku, pod warunkiem, że unikniesz podwójnych.

ecv
źródło
Dzięki Kevin za pomoc.
Rachit 12.12.12
2

po prostu użyj printf

zamiast

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

użyj printf, aby zastąpić token zmiennej zmienną rozwiniętą.

Na przykład:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
AndrewD
źródło
To jest zepsute; printftak naprawdę nic tu nie kupuje, a brak cytowania zastępowania poleceń powoduje nowe problemy z cytowaniem.
tripleee
0

Zmienne mogą zawierać pojedyncze cudzysłowy.

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

repo forall -c $myvar
użytkownik3734617
źródło
Mogą, ale nie jest to właściwy sposób na zrobienie tego - wciąż powinieneś umieszczać "$myvar"podwójne cudzysłowy.
tripleee
-2

Czy to ci odpowiada?

eval repo forall -c '....$variable'
drgnfr
źródło
8
Czy to dla ciebie? To pytanie ma pięć lat i ma już kilka odpowiedzi, nawet zaakceptowanych ...
Nico Haase