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

Chcę uruchomić polecenie ze skryptu bash , który ma pojedyncze cudzysłowy i kilka innych poleceń wewnątrz pojedynczych cudzysłowów i zmiennej.

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

W tym formacie $ jest unikalny i zmienna nie jest rozwijana.

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

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

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

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

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

Jeśli zamienię wartość w miejsce zmiennej, polecenie zostanie wykonane poprawnie.

Proszę, powiedz mi, gdzie idę źle.
Author: codeforester, 2012-12-10

7 answers

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

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

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

Konkatenacja słów jest po prostu dokonywana przez zestawienie. Jak możesz zweryfikować, każda z powyższych linii jest pojedynczym słowem do powłoki. Cudzysłowy (pojedyncze lub podwójne cudzysłowy, w zależności od sytuacji) nie izolują słów. Są one używane tylko do wyłączania interpretacji różnych znaków specjalnych, takich jak spacje, $, ;... Dobry samouczek na temat cytowania zobacz odpowiedź Marka Reeda. Również istotne: które znaki muszą być uciekane w bash?

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 konkatenacji fragmentów SQL (SQL injection!).

Zazwyczaj można mieć placeholdery w poleceniu i dostarczać polecenie wraz ze zmiennymi aby callee mógł je odbierać z listy argumentów wywołania.

Na przykład, poniżej jest bardzo niebezpieczne. NIE RÓB TEGO

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

Jeśli zawartość $myvar jest niezaufana, oto exploit:

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

Zamiast powyższego wywołania, użyj argumentów pozycyjnych. Poniższe 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 kleszczy w przypisaniu do script, co oznacza, że jest rozumiane dosłownie, bez rozszerzania zmiennych lub każda inna forma interpretacji.

 703
Author: Jo So,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-19 14:19:27

Komenda repo nie ma znaczenia, jaki rodzaj cudzysłowu otrzymuje. Jeśli potrzebujesz rozszerzenia parametrów, użyj podwójnych cudzysłowów. Jeśli to oznacza, że będziesz musiał odwrotny ukośnik wielu rzeczy, użyj pojedynczych cudzysłowów dla większości z nich, a następnie wyłamaj się z nich i przejdź do podwójnych dla 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.

Kiedy uruchamiasz polecenie z powłoki, to co to polecenie otrzymuje jako argumenty jest tablicą zakończonych znakiem null struny. Łańcuchy te mogą zawierać absolutnie dowolny znak nie-null.

Ale kiedy powłoka buduje tablicę łańcuchów z wiersza poleceń, interpretuje niektóre znaki specjalnie; ma to na celu ułatwienie (w rzeczy samej, możliwe) pisania 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". Ale argument może jednak mieć w sobie spacje; wystarczy, że jakiś sposób, by powiedzieć skorupie, że tego chcesz.

Można użyć odwrotnego ukośnika przed dowolnym znakiem (w tym spacją lub innym odwrotnym ukośnikiem), aby powiedzieć powłoce, aby traktowała ten znak dosłownie. Ale podczas gdy można zrobić coś takiego:

echo \”That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

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

Znaki podwójnego cudzysłowu nazywane są "cudzysłowami grupującymi". Zapobiegają dzikim kartom i aliasom przed są rozszerzane, ale przede wszystkim służą do umieszczania spacji w słowie. Inne rzeczy, takie jak rozszerzenie parametru i polecenia (rodzaje rzeczy sygnalizowane przez $) nadal się zdarzają. I oczywiście, jeśli chcesz dosłownego podwójnego cudzysłowu wewnątrz podwójnych cudzysłowów, musisz go ukośnikiem wstecznym: {]}

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

Pojedyncze cudzysłowy są bardziej drakońskie. Wszystko między nimi jest brane całkowicie dosłownie, w tym ukośniki. Nie ma absolutnie żadnego sposobu, aby uzyskać dosłowny pojedynczy cytat wewnątrz singla cytaty.

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

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

Więc to łatwiejsze - dużo mniej ukośników wstecznych, chociaż Sekwencja close-single-quote, backslash-dosłowny - single-quote, open-single-quote wymaga trochę przyzwyczajenia.

Współczesne muszle mają dodano inny styl cytowania nie określony przez standard POSIX, w którym wiodący pojedynczy cudzysłów jest poprzedzony znakiem dolara. Tak cytowane ciągi znaków mają podobne konwencje do literałów ciągów w standardowej wersji języka C języka ANSI i dlatego są czasami nazywane "ciągami ANSI" i $'...' para "cytaty ANSI". W obrębie takich ciągów powyższa Rada o przyjmowaniu ukośników wstecznych nie ma już zastosowania. Zamiast tego znów stają się wyjątkowe - nie tylko można dodać dosłowny pojedynczy cudzysłów lub odwrotny ukośnik, poprzedzając go ukośnikiem, ale powłoka rozszerza również znaki escapes ANSI C (jak {[9] } dla nowej linii, {[10] } dla tabulacji i \xHH dla znaku z kodem szesnastkowym HH). W przeciwnym razie zachowują się one jak pojedyncze cytowane ciągi: nie zachodzi Zamiana parametru ani polecenia:

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

Ważne jest, aby pamiętać, że pojedynczy łańcuch otrzymany jako argument echo jest dokładnie to samo we wszystkich tych przykładach. Po zakończeniu parsowania przez powłokę wiersza poleceń, nie ma możliwości, aby uruchamiane polecenie wskazywało, w jaki sposób było cytowane. Nawet gdyby chciał.

 109
Author: Mark Reed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-09 13:38:12

Poniżej jest to, co dla mnie działa -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
 3
Author: Manmohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-04 11:11:31

EDIT: (Jak w komentarzach:)

Przyglądałem się temu od tamtej pory. Miałem szczęście, że miałem repo. Nadal nie jest dla mnie jasne, czy musisz umieszczać swoje polecenia między pojedynczymi cudzysłowami na siłę. Przyjrzałem się składni repo i nie sądzę, że musisz. Możesz użyć podwójnych cudzysłowów wokół polecenia, a następnie użyć dowolnych pojedynczych i podwójnych cudzysłowów, których potrzebujesz w środku, pod warunkiem, że unikniesz podwójnych.

 2
Author: ecv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-11 14:53:10

Wystarczy użyć printf

Zamiast

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

Użyj printf, aby zastąpić token zmiennej zmienną rozszerzoną.

Na przykład:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
 2
Author: AndrewD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-21 02:48:50

Zmienne mogą zawierać pojedyncze cudzysłowy.

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

repo forall -c $myvar
 0
Author: user3734617,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-24 02:24:02

Czy to działa dla Ciebie?

eval repo forall -c '....$variable'
 -3
Author: drgnfr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 11:18:10