Radzenie sobie z wieloma poziomami cytowania (tak naprawdę, wieloma poziomami parsowania / interpretacji) może być skomplikowane. Pomaga pamiętać o kilku rzeczach:
- Każdy „poziom cytowania” może potencjalnie obejmować inny język.
- Zasady cytowania różnią się w zależności od języka.
- W przypadku więcej niż jednego lub dwóch zagnieżdżonych poziomów zwykle najłatwiej jest pracować „od dołu do góry” (tj. Od środka do najbardziej na zewnątrz).
Poziomy cytowania
Spójrzmy na twoje przykładowe polecenia.
pgrep -fl java | grep -i datanode | awk '{print $1}'
Twoje pierwsze przykładowe polecenie (powyżej) używa czterech języków: twojej powłoki, wyrażenia regularnego w pgrep , wyrażenia regularnego w grep (które mogą być inne niż język wyrażenia regularnego w pgrep ) i awk . Istnieją dwa poziomy interpretacji: powłoka i jeden poziom za powłoką dla każdego z zaangażowanych poleceń. Istnieje tylko jeden wyraźny poziom cytowania (cytowanie powłoki w awk ).
ssh host …
Następnie dodałeś poziom ssh na górze. Jest to faktycznie kolejny poziom powłoki: ssh nie interpretuje samego polecenia, przekazuje je powłoce na zdalnym końcu (przez (np.) sh -c …
) I ta powłoka interpretuje ciąg znaków.
ssh host "sudo su user -c …"
Następnie zapytałeś o dodanie kolejnego poziomu powłoki za pomocą su (via sudo , który nie interpretuje argumentów poleceń, więc możemy go zignorować). W tym momencie masz trzy poziomy zagnieżdżania ( awk → powłoka, powłoka → powłoka ( ssh ), powłoka → powłoka ( su user -c ), więc radzę stosować podejście „od dołu do góry”. twoje muszle są kompatybilne z Bourne (np. sh , ash , dash , ksh , bash , zsh itp.) Niektóre inne rodzaje muszli ( ryby , rcitp.) może wymagać innej składni, ale metoda nadal obowiązuje.
Dół, góra
- Sformułuj ciąg, który chcesz reprezentować na najbardziej wewnętrznym poziomie.
- Wybierz mechanizm cytowania z repertuaru cytowań następnego najwyższego języka.
- Podaj żądany ciąg zgodnie z wybranym mechanizmem cytowania.
- Często istnieje wiele wariantów zastosowania mechanizmu cytowania. Robienie tego ręcznie jest zwykle kwestią praktyki i doświadczenia. Robiąc to programowo, zazwyczaj najlepiej jest wybrać najłatwiejszy do zrobienia (zwykle „najbardziej dosłowny” (najmniej ucieczek)).
- Opcjonalnie możesz użyć wynikowego cudzysłowu z dodatkowym kodem.
- Jeśli nie osiągnąłeś jeszcze pożądanego poziomu cytowania / interpretacji, weź otrzymany ciąg cytowany (plus dowolny dodany kod) i użyj go jako łańcucha początkowego w kroku 2.
Cytując Semantykę Zmieniaj się
Należy pamiętać, że każdy język (poziom cytowania) może nadawać nieco inną semantykę (lub nawet drastycznie inną semantykę) temu samemu znakowi cytowania.
Większość języków ma „dosłowny” mechanizm cytowania, ale różnią się dokładnie pod względem dosłowności. Pojedynczy cytat z powłok podobnych do Bourne'a jest w rzeczywistości dosłowny (co oznacza, że nie można go używać do cytowania samego znaku cytowania). Inne języki (Perl, Ruby) są mniej dosłowne w tym, że interpretują pewne sekwencje backslash w pojedynczych regionach cytowane dosłownie non-(konkretnie, \\
a \'
wynik w \
i '
, ale inne sekwencje ukośnik odwrotny to rzeczywiście dosłowny).
Będziesz musiał przeczytać dokumentację dla każdego języka, aby zrozumieć zasady cytowania i ogólną składnię.
Twój przykład
Najbardziej wewnętrzny poziom twojego przykładu to program awk .
{print $1}
Zamieścisz to w linii poleceń powłoki:
pgrep -fl java | grep -i datanode | awk …
Musimy chronić (przynajmniej) w przestrzeni i $
w awk programie. Oczywistym wyborem jest użycie pojedynczego cudzysłowu w powłoce wokół całego programu.
Istnieją jednak inne opcje:
{print\ \$1}
bezpośrednio uciec z przestrzeni i $
{print' $'1}
pojedynczy cytat tylko przestrzeń i $
"{print \$1}"
podwójnie zacytuj całość i uciec $
{print" $"1}
podwójny cudzysłów tylko spacja i $
to może nieco wyginać reguły (nieosłonięte $
na końcu łańcucha podwójnego cudzysłowu jest dosłowne), ale wydaje się, że działa w większości powłok.
Gdyby program używał przecinka między otwartymi i zamkniętymi nawiasami klamrowymi, musielibyśmy również zacytować lub uciec albo przecinek, albo nawiasy klamrowe, aby uniknąć „rozszerzenia nawiasów klamrowych” w niektórych powłokach.
Wybieramy '{print $1}'
i osadzamy go w pozostałej części „kodu” powłoki:
pgrep -fl java | grep -i datanode | awk '{print $1}'
Następnie chciałeś uruchomić to za pomocą su i sudo .
sudo su user -c …
su user -c …
jest podobny some-shell -c …
(oprócz działania pod innym identyfikatorem UID), więc su po prostu dodaje kolejny poziom powłoki. sudo nie interpretuje swoich argumentów, więc nie dodaje żadnych poziomów cytowania.
Potrzebujemy innego poziomu powłoki dla naszego ciągu poleceń. Możemy ponownie wybrać pojedyncze cytaty, ale musimy specjalnie traktować istniejące pojedyncze cytaty. Zwykły sposób wygląda następująco:
'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Są tutaj cztery ciągi, które powłoka będzie interpretować i konkatenować: pierwszy ciąg pojedynczego cudzysłowu ( pgrep … awk
), pojedynczy cytat ze znakiem ucieczki, program awk z pojedynczym cudzysłowem , inny pojedynczy cytat ze znakiem ucieczki.
Istnieje oczywiście wiele alternatyw:
pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1}
uciec od wszystkiego, co ważne
pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}
to samo, ale bez zbędnych białych znaków (nawet w programie awk !)
"pgrep -fl java | grep -i datanode | awk '{print \$1}'"
podwójnie zacytuj całość, ucieknij $
'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"
twoja odmiana; nieco dłuższy niż zwykle ze względu na użycie podwójnych cudzysłowów (dwa znaki) zamiast znaków ucieczki (jeden znak)
Użycie różnych cytatów na pierwszym poziomie pozwala na inne warianty na tym poziomie:
'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
'pgrep -fl java | grep -i datanode | awk {print\ \$1}'
Osadzenie pierwszej odmiany w wierszu polecenia sudo / * su * daje:
sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Możesz użyć tego samego łańcucha w dowolnym innym kontekście pojedynczego poziomu powłoki (np ssh host …
.).
Następnie dodałeś poziom ssh na górze. Jest to w rzeczywistości kolejny poziom powłoki: ssh nie interpretuje samego polecenia, ale przekazuje je powłoce na zdalnym końcu (przez (np.) sh -c …
) I ta powłoka interpretuje ciąg znaków.
ssh host …
Proces jest taki sam: weź ciąg, wybierz metodę cytowania, użyj go, umieść go.
Ponowne użycie pojedynczych cudzysłowów:
'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Teraz jest jedenaście ciągów, które są interpretowane i łączone:, 'sudo su user -c '
uniknął pojedynczego cytatu, 'pgrep … awk '
uniknął pojedynczego cytatu, uniknął odwrotnego ukośnika, dwóch unikniętych pojedynczych cytatów, pojedynczego cytowanego programu awk , unikniętego pojedynczego cytatu, unikniętego odwrotnego ukośnika i ostatniego unikniętego pojedynczego cytatu .
Ostateczna forma wygląda następująco:
ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Pisanie ręczne jest trochę niewygodne, ale dosłowny charakter pojedynczego cytowania powłoki ułatwia automatyzację niewielkich zmian:
#!/bin/sh
sq() { # single quote for Bourne shell evaluation
# Change ' to '\'' and wrap in single quotes.
# If original starts/ends with a single quote, creates useless
# (but harmless) '' at beginning/end of result.
printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}
# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }
ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"
ssh host "$(sq "$s2")"