ssh를 통한 명령에서 true로 OR


15

pkill -fssh를 통해 원격으로 실행하려고 할 때 가능한 오류 코드를 삭제하려고하면 (프로세스가 없으면 스크립트의 나머지 부분으로 진행) || true예상대로 작동하지 않습니다.

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255

따옴표 사이의 명령이 아니라 255를 반환 하는 것이 ssh 라고 가정 하지만 그 이유는 무엇입니까?

답변:


29

ssh255 종료 상태를 리턴하는 것은 그 자체라는 가정 이 맞습니다. sshman 페이지는 말한다 :

ssh는 원격 명령의 종료 상태 또는 오류가 발생한 경우 255로 종료됩니다.

간단하게 실행했다면 ssh pi@10.20.0.10 "pkill -f asdf", " 일치하는 프로세스가 없습니다 "상태에 1해당 하는 종료 상태가 나타납니다 .pkill

어려운 부분은 실행할 때 SSH에서 오류가 발생 하는 이유 를 이해 하는 것입니다

ssh pi@10.20.0.10 "pkill -f asdf || true"

SSH 원격 명령

SSH 서버는 셸을 시작하여 원격 명령을 실행합니다. 다음은 이에 대한 예입니다.

$ ssh server "ps -elf | tail -5"
4 S root     35323  1024 12  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony [priv]
5 S anthony  35329 35323  0  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony@notty
0 S anthony  35330 35329  0  80   0 - 28283 do_wai 12:01 ?        00:00:00 bash -c ps -elf | tail -5
0 R anthony  35341 35330  0  80   0 - 40340 -      12:01 ?        00:00:00 ps -elf
0 S anthony  35342 35330  0  80   0 - 26985 pipe_w 12:01 ?        00:00:00 tail -5

기본 쉘은 bash원격 명령이며 단순한 명령이 아니라 파이프 라인 ( 제어 연산자로 구분 된 하나 이상의 명령 시퀀스)입니다 |.

Bash 쉘은 -c옵션에 의해 전달되는 명령이 간단한 명령 인 경우 실제로 새 프로세스를 분기하지 않고 (예 : exec추가 단계를 거치지 않고 직접 간단한 명령을 수행하여) 최적화 할 수 있음을 알기에 충분히 영리 합니다. 의 fork는 전에 보내고 exec이야. 다음은 원격 단순 명령 ( ps -elf이 경우) 을 실행할 때 발생하는 예입니다 .

$ ssh server "ps -elf" | tail -5
1 S root     34740     2  0  80   0 -     0 worker 11:49 ?        00:00:00 [kworker/0:1]
1 S root     34762     2  0  80   0 -     0 worker 11:50 ?        00:00:00 [kworker/0:3]
4 S root     34824  1024 31  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony [priv]
5 S anthony  34829 34824  0  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony@notty
0 R anthony  34830 34829  0  80   0 - 40340 -      11:51 ?        00:00:00 ps -elf

이전 에이 동작을 보았지만 이 AskUbuntu answer 이외의 더 나은 참조를 찾을 수 없었습니다 .

pkill behavior

때문에 pkill -f asdf || true간단한 명령 (그것은이다 아닌 명령 목록 )을 실행할 때, 위의 최적화는 그렇게 발생하지 않을 수 ssh pi@10.20.0.10 "pkill -f asdf || true"sshd프로세스 포크와 임원을 bash -c "pkill -f asdf || true".

ctx의 답변이 지적했듯이 pkill자체 프로세스는 종료하지 않습니다. 그러나, 그것은 것입니다 누구의 명령 라인과 일치하는 다른 프로세스 죽이기 -f패턴. 이 bash -c명령은이 패턴과 일치하므로이 프로세스 (자체의 부모)가 종료됩니다.

그런 다음 SSH 서버는 원격 명령을 실행하기 위해 시작한 쉘 프로세스가 예기치 않게 종료 된 것을 확인하여 SSH 클라이언트에 오류를보고합니다.


1
대답은 제대로에서와 같이 문제가 소스를 식별하는 동안 pkill의 인수 목록은 정규 표현식을 일치하기 때문에 부모 쉘 프로세스를 종료를, 나는 용어 이의를 제기 할 수 있습니다 : x || y입니다 하지 복합 명령 , 그것은의 명령 목록 .
Stéphane Chazelas

@ StéphaneChazelas 의견을 보내 주셔서 감사합니다. 조건부 구문을 복합 명령 으로 Bash에 포함 시켰지만 x||y명령 목록 으로 고려하는 것이 논리적으로 일관성이 있음에 동의 합니다. 이제 다양한 POSIX 정의에 대한 링크를 포함하도록 답변을 편집했습니다.
Anthony G-Monica에 대한 정의

1
일반적인 경우에는 명령 목록 이기 때문에 (잠재적으로) 실행할 다른 명령이 있기 때문에 최적화 할 수 없습니다 . 에서 zsh/ ksh93/ FreeBSD의 sh, false || pkill -f asdfpkill쉘 프로세스에서 실행. bash간단한 명령이 하나만있을 때만 최적화를 수행합니다. true; pkill -f asdf또한 문제가 될 것입니다.
Stéphane Chazelas

9

당신의 원격 명령은 스스로를 죽입니다 :

$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true

pgrep 및 pkill은 자체 프로세스를 무시하지만 -f 플래그를 사용하면 상위 쉘을 찾습니다.

$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true

말이 되네요! bash -c 'pgrep -af asdf'(포함하지 않는 || true) 않습니다 하지 자체를 찾을 수 있습니다. 왜 안돼? 있습니다 -f.
Gauthier

2
@Gauthier 사실,이 경우 Bash는 명령이 단순한 명령 (복합 명령이 아님)임을 깨닫기에 실제로 영리합니다. 그래서 실제로 새로운 프로세스를 포기하지 않음으로써 최적화됩니다. 나는 전에 비슷한 행동을 겪었던 것을 기억합니다.
Anthony G-Monica에 대한 정의

3

pkill에게 "asdf"와 일치하는 것을 죽 이도록 요청하십시오. [a] sdf와 일치하도록 지시해야합니다. 그래야 여전히 "asdf"라는 이름을 찾게되지만 자체는 보이지 않습니다 (asdf와 [a] sdf를 정렬하면 s가]와 일치 함을 알 수 있습니다. s.)

ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'

grep / egrep / awk / etc와 함께 사용되는 일반적인 트릭입니다.

ps -ef | grep "something"  # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself

# why it works:
# the commandline contains:     ps -ef | grep [s]omething
# and grep tries to find:                      something

이 트릭은 오래되었고, 수십 년 전에 유닉스 FAQ에서 보았습니다 (아직도 잘 읽습니다!)

"자동화"하는 것은 쉽지 않지만 일반적으로 변수 문자열 regexp = "something"을 grep해야 할 때마다 다음을 수행 할 수 있습니다.

grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')" 
#  if regexp="something",  it does: grep "[s]omething"
#  if regexp="otherthing", it does: grep "[o]therthing"
#  if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...

참고 : 내 '실패'grep 예, 이미 정규 표현식을 그대로 유지했을 수 있습니다 (a, b 또는 c는 명령 줄의 ']'과 일치하지 않습니다) . 그러나 정규 표현식을 테스트하는 것은 쉽지 않습니다. 일반적으로 트릭이 작동합니다. 자동화하는 것이 대부분 작동합니다. 그렇지 않은 경우, 일부 영리한 해킹 (또는 수동 개입)이 필요합니다.
Olivier Dulac

또한, (abc)?(def)?해야 할 것이다 ([a]bc)?([d]ef)?... 당신 수 없습니다 구문 분석 정규식 정규식! > :-)
wizzwizz4

@ wizzwizz4 알아요. 그러나 귀하의 예는 이미 일치하지 않습니다. 이것은 복잡한 것입니다. 저는 더 간단한 경우를위한 간단한 해결책을 제공했습니다
Olivier Dulac

@ 난 이미 내 첫 코멘트 ... 그렇게 말할 wizzwizz4
올리비에 Dulac
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.