여러 수준의 인용 (실제로 여러 수준의 파싱 / 통역)을 다루는 것은 복잡 할 수 있습니다. 몇 가지 사항을 명심하십시오.
- 각“인용 수준”에는 다른 언어가 포함될 수 있습니다.
- 인용 규칙은 언어마다 다릅니다.
- 하나 또는 두 개 이상의 중첩 된 레벨을 처리 할 때 일반적으로 "아래에서 위로"(즉, 가장 안쪽에서 가장 바깥 쪽) 작업하는 것이 가장 쉽습니다.
인용 수준
예제 명령을 살펴 보겠습니다.
pgrep -fl java | grep -i datanode | awk '{print $1}'
첫 번째 예제 명령 (위)은 셸, pgrep 의 정규식, grep 의 정규식 ( pgrep 의 정규식 언어와 다를 수 있음 ) 및 awk의 네 가지 언어를 사용합니다 . 관련된 두 가지 해석 수준이 있습니다 : 셸과 관련된 각 명령에 대한 셸 뒤의 한 수준. 인용의 명시 적 수준은 하나뿐입니다 ( awk에 쉘 인용 ).
ssh host …
다음으로 ssh 레벨 을 맨 위에 추가했습니다 . 이것은 실제로 또 다른 쉘 레벨입니다 .ssh 는 명령 자체를 해석하지 않으며, 원격 엔드의 쉘 (예 :을 통해 sh -c …
)에 전달하고 해당 쉘은 문자열을 해석합니다.
ssh host "sudo su user -c …"
그런 다음 su ( 명령 인수를 해석하지 않으므로 sudo 를 통해)를 사용하여 중간에 다른 쉘 레벨을 추가하는 것에 대해 물었 습니다. 이 시점에서 세 가지 수준의 중첩이 진행되고 ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c )“bottom, up”접근 방식을 사용하는 것이 좋습니다. 쉘은 Bourne과 호환됩니다 (예 : sh , ash , dash , ksh , bash , zsh 등) 다른 종류의 쉘 ( fish , rc등)에는 다른 구문이 필요할 수 있지만이 방법은 여전히 적용됩니다.
아래, 위로
- 가장 안쪽에 표시하려는 문자열을 공식화하십시오.
- 가장 높은 언어의 인용 레퍼토리에서 인용 메커니즘을 선택하십시오.
- 선택한 인용 메커니즘에 따라 원하는 문자열을 인용하십시오.
- 어떤 인용 메커니즘을 적용하는 방법이 많은 경우가 종종 있습니다. 손으로 직접하는 것은 일반적으로 연습과 경험의 문제입니다. 프로그래밍 방식으로 수행 할 때는 일반적으로 가장 쉬운 방법을 선택하는 것이 가장 좋습니다 (일반적으로 "가장 문자 그대로"(가장 이스케이프 처리)).
- 선택적으로 추가 코드와 함께 인용 된 결과 문자열을 사용하십시오.
- 원하는 인용 / 해석 수준에 아직 도달하지 않은 경우 인용 된 문자열 (추가 된 코드 포함)을 가져 와서 2 단계에서 시작 문자열로 사용하십시오.
인용 시맨틱 바리
여기서 명심해야 할 것은 각 언어 (인용 수준)가 동일한 인용 문자에 대해 약간 다른 의미를 부여 할 수도 있다는 것입니다.
대부분의 언어에는 "리터럴"인용 메커니즘이 있지만 문자 그대로 정확히 다릅니다. Bourne과 같은 쉘의 작은 따옴표는 실제로 리터럴입니다 (따라서 작은 따옴표 문자 자체를 인용하는 데 사용할 수 없음). 다른 언어 (펄, 루비) 이하가 해석하는 것을 문자 그대로입니다 일부 가 아닌 문자 그대로 하나의 인용 지역 내부의 백 슬래시 시퀀스를 (특히, \\
과 \'
에서 결과 \
및 '
하지만, 다른 백 슬래시 시퀀스는 실제로 문자입니다).
인용 규칙과 전체 구문을 이해하려면 각 언어에 대한 설명서를 읽어야합니다.
당신의 예
예제의 가장 안쪽 레벨은 awk 프로그램입니다.
{print $1}
이것을 쉘 명령 행에 포함 시키려고합니다 :
pgrep -fl java | grep -i datanode | awk …
우리는 (최소한) 공간과 보호해야 $
에서 AWK의 프로그램을. 명백한 선택은 전체 프로그램 주위의 셸에서 작은 따옴표를 사용하는 것입니다.
그래도 다른 선택이 있습니다.
{print\ \$1}
직접 공간을 탈출하고 $
{print' $'1}
작은 따옴표 만 공백과 $
"{print \$1}"
전체를 이중 인용하고 탈출 $
{print" $"1}
큰 따옴표 만 공백과 $
이것은 규칙을 약간 구부릴 수 있습니다 ( $
큰 따옴표로 묶인 문자열의 끝에서 이스케이프 처리 된 것은 리터럴 임), 대부분의 쉘에서 작동하는 것 같습니다.
프로그램이 열린 중괄호와 닫는 중괄호 사이에 쉼표를 사용한 경우 일부 쉘에서 "중괄호 확장"을 피하기 위해 쉼표 나 중괄호를 인용하거나 이스케이프해야합니다.
우리는 '{print $1}'
그것을 나머지 쉘“code”에 골라 넣습니다.
pgrep -fl java | grep -i datanode | awk '{print $1}'
다음으로 su 와 sudo 를 통해 이것을 실행하고 싶었습니다 .
sudo su user -c …
su user -c …
some-shell -c …
다른 UID로 실행되는 것을 제외하고 는 su 와 비슷 하므로 su 는 다른 쉘 레벨을 추가합니다. sudo 는 인수를 해석하지 않으므로 인용 레벨을 추가하지 않습니다.
명령 문자열에 다른 쉘 레벨이 필요합니다. 작은 따옴표를 다시 선택할 수 있지만 기존의 작은 따옴표를 특별하게 처리해야합니다. 일반적인 방법은 다음과 같습니다.
'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
쉘이 해석하고 연결할 네 개의 문자열이 있습니다 : 첫 번째 작은 따옴표로 묶인 문자열 ( pgrep … awk
), 이스케이프 된 작은 따옴표, 작은 따옴표로 묶인 awk 프로그램, 또 다른 이스케이프 된 작은 따옴표.
물론 많은 대안이 있습니다 :
pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1}
중요한 모든 것을 탈출
pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}
동일하지만 불필요한 공백은 없습니다 ( awk 프로그램에서도!)
"pgrep -fl java | grep -i datanode | awk '{print \$1}'"
모든 것을 큰 따옴표로 묶고 $
'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"
당신의 변형; 이스케이프 대신 큰 따옴표 (2 문자)를 사용하여 일반적인 방법보다 약간 길다 (1 문자)
첫 번째 수준에서 다른 따옴표를 사용하면이 수준에서 다른 변형이 가능합니다.
'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
'pgrep -fl java | grep -i datanode | awk {print\ \$1}'
sudo / * su * 명령 행에 첫 번째 변형을 포함하면 다음과 같습니다.
sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
다른 단일 쉘 레벨 컨텍스트 (예 :)에서 동일한 문자열을 사용할 수 있습니다 ssh host …
.
다음으로 ssh 레벨 을 맨 위에 추가했습니다 . 이것은 실제로 또 다른 쉘 레벨입니다. ssh 는 명령 자체를 해석하지 않지만 원격 엔드의 쉘 (예 :)을 통해 전달 sh -c …
하고 해당 쉘은 문자열을 해석합니다.
ssh host …
과정은 동일합니다. 문자열을 가져 와서 인용 방법을 선택하여 사용하고 포함하십시오.
작은 따옴표를 다시 사용하여 :
'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
이제 해석과 연결된 11 개 문자열이 있습니다 'sudo su user -c '
, 작은 따옴표를 탈출 'pgrep … awk '
, 작은 따옴표는 이스케이프 백 슬래시, 두 개의 작은 따옴표를 탈출, 단일 인용 탈출 된 awk 프로그램, 단일 인용, 이스케이프 백 슬래시를 탈출하고, 최종 단일 인용 탈출 .
최종 양식은 다음과 같습니다.
ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
이것은 손으로 타이핑하기에는 다소 까다 롭지 만 쉘의 작은 따옴표는 문자 그대로의 특성으로 약간의 변형을 쉽게 자동화 할 수 있습니다.
#!/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")"