![SSH를 통해 실행된 bash 스크립트가 잘못된 종료 코드 0을 반환합니다.](https://linux55.com/image/144799/SSH%EB%A5%BC%20%ED%86%B5%ED%95%B4%20%EC%8B%A4%ED%96%89%EB%90%9C%20bash%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EA%B0%80%20%EC%9E%98%EB%AA%BB%EB%90%9C%20%EC%A2%85%EB%A3%8C%20%EC%BD%94%EB%93%9C%200%EC%9D%84%20%EB%B0%98%ED%99%98%ED%95%A9%EB%8B%88%EB%8B%A4..png)
SSH를 통해 다양한 컴퓨터에서 스크립트를 실행하는 프로세스를 자동화하려고 합니다. 출력 및 반환 코드(오류 감지용)를 캡처하는 것이 중요합니다.
종료 코드 설정은 예상대로 명시적으로 작동합니다.
~$ ssh host exit 5 && echo OK || echo FAIL
FAIL
그러나 비정상 종료를 알리는 쉘 스크립트가 있는 경우 ssh는 항상 0을 반환합니다(문자열을 통해 시뮬레이션된 스크립트 실행).
~$ ssh host sh -c 'exit 5' && echo OK || echo FAIL
OK
대화형 셸의 호스트 시스템에서 정확히 동일한 스크립트를 실행하면 정상적으로 작동합니다.
~$ sh -c 'exit 5' && echo OK || echo FAIL
FAIL
왜 이런 일이 일어나는지 혼란스러워요. bash의 반환 코드를 전파하도록 ssh에 어떻게 지시합니까? 아마도 원격 스크립트를 변경하지 않을 것입니다.
공개 키 인증을 사용하고 있으며 개인 키가 잠금 해제되었습니다. 사용자 상호 작용이 필요하지 않습니다. 모든 시스템은 Ubuntu 18.04입니다. 애플리케이션 버전은 다음과 같습니다.
OpenSSH_7.6p1 Ubuntu-4ubuntu0.1, OpenSSL 1.0.2n 7 Dec 2017
GNU bash, Version 4.4.19(1)-release (x86_64-pc-linux-gnu)
참고: 이 질문은 유사해 보이는 다음 질문과 다릅니다.
답변1
에서 언급했듯이답변거기에 리모컨이 sh
실행되지 않습니다 exit 5
. 오직 exit
:
$ ssh test sh -x -c 'exit 5'; echo $?
+ exit
0
예를 들어 여기서 무슨 일이 일어나고 있는지 설명합니다.이 답변:
ssh
원격 셸을 실행하고 통과끈그것에,아니요매개변수 목록.
우리가 실행할 때 ssh host sh -c 'exit 5'
:
- 로컬 쉘은 작은따옴표(견적 삭제);
- 클라이언트는
ssh
매개변수host
,sh
및-c
을 가져옵니다exit 5
. 이를 문자열로 연결하여 원격 호스트로 보냅니다. - 원격 호스트에서
ssh
셸을 호출하고 문자열을 전달합니다sh -c exit 5
. - 아래와 같이 원격 셸이 호출되고 옵션이
sh
전달됩니다 .-c
exit
명령 문자열,5
그리고명령 이름.
끝에 단어를 추가하면 exit 5
추가 인수로만 전달됩니다 sh
. 쉘에서 인식되지 않는 것과 관련된 오류는 없습니다.
$ ssh test sh -x -c 'exit 5' a b c; echo $?
+ exit
0
strace
확인은 5
여기에 제공된 명령 문자열의 일부가 아닙니다 sh
. 이는 인수입니다.
$ ssh test strace -e execve sh -c 'exit 5'; echo $?
execve("/usr/bin/sh", ["sh", "-c", "exit", "5"], 0x7ffc0d744c38 /* 14 vars */) = 0
+++ exited with 0 +++
0
원격 호스트에서 예상대로 실행 하려면 sh -c 'command'
따옴표를 올바르게 보내고 있는지도 확인해야 합니다.
$ ssh test "sh -x -c 'exit 5'"; echo $?
+ exit 5
5
현재 문제와 관련이 없는 전체 원격 명령을 명시적으로 참조하려면 다음과 같이 작성할 수 있습니다.
$ ssh test sh -x -c "'exit 5'"; echo $?
+ exit 5
5
내부 따옴표를 두 번 인용하는 대신 백슬래시로 이스케이프하는 것도 가능합니다.
명령에 대한 의견 ssh host sh -c ':; exit 5'
(질문에 대한 의견) 그것이 하는 일은:
$ ssh test sh -x -c ':; exit 5'; echo $?
+ :
5
즉 exit 5
, 가 아닌 셸에 의해 실행됩니다 sh
. 다시 sh
원하는 코드로 종료합니다.
$ ssh test sh -x -c "':; exit 5'"; echo $?
+ :
+ exit 5
5
답변2
귀하가 사용한 명령을 사용하여 이 문제를 재현할 수 있었고 원격 명령을 따옴표로 묶어 문제를 해결할 수 있었습니다. 내 테스트 사례는 다음과 같습니다.
#!/bin/bash -x
echo 'Unquoted Test:'
ssh evil sh -x -c exit 5 && echo OK || echo FAIL
echo 'Quoted Test 1:'
ssh evil sh -x -c 'exit 5' && echo OK || echo FAIL
echo 'Quoted Test 2:'
ssh evil 'sh -x -c "exit 5"' && echo OK || echo FAIL
결과는 다음과 같습니다.
bash-[540]$ bash -x test.sh
+ echo 'Unquoted Test:'
Unquoted Test:
+ ssh evil sh -x -c exit 5
+ exit
+ echo OK
OK
+ echo 'Quoted Test 1:'
Quoted Test 1:
+ ssh evil sh -x -c 'exit 5'
+ exit
+ echo OK
OK
+ echo 'Quoted Test 2:'
Quoted Test 2:
+ ssh evil 'sh -x -c "exit 5"'
+ exit 5
+ echo FAIL
FAIL
1차 테스트와 2차 테스트에서는 5
기대한대로 전달되지 않는 것 같았습니다. exit
사라지는 것 같습니다. 그것은 불평하지도 exit
, sh
불평하지도 5: command not found
, ssh
불평하지도 않을 것입니다.
세 번째 테스트에서는 exit 5
두 번째 테스트와 동일한 내용이 원격 호스트에서 실행되는 더 큰 명령에서 참조됩니다. 이렇게 하면 가 5
전달되고 둘 다 exit
옵션으로 실행됩니다. 두 번째와 세 번째 테스트의 차이점은 전체 명령 및 매개변수 세트가 단일 명령 매개변수로 참조되는 원격 호스트로 전송된다는 것입니다.-c
sh
ssh
답변3
다른 답변은 주어진 예보다는 이 질문에 매우 잘 대답합니다. 내 실제 애플리케이션은 더 복잡하며 일련의 스크립트와 하위 프로세스가 포함됩니다. 다음은 실행하려는 간단한 예제 스크립트입니다.
#!/bin/bash
sub-process-that-fails
# store and echo returncode for debug purposes
rc=$?
echo $rc
exit $rc
원격으로 실행되는 셸이 실제로 대시가 아닌 bash인지 확인하기 위해(@JeffSchaller가 지적했듯이) 다음과 같이 스크립트를 호출해 보았습니다.
~$ ssh -t -t host /bin/bash -x /srv/scripts/run.sh ; echo $?
결과적으로 다음과 같은 이상한 출력이 발생합니다.
+ sub-process-that-fails
+ rc=5
+ echo 5
5
+ exit 5
0
몇 시간 동안 탐색한 후에 이것이 무엇을 하는지는 bash가 종료될 때 모든 하위 프로세스를 종료한다는 것을 trap 'kill 0' EXIT
알았습니다 . .bashrc
Bash 추적에는 이 트랩의 실행이 표시되지 않는 것 같습니다. 트랩을 래퍼 스크립트로 옮겼습니다. 이제 실제로 실행되는 내용을 볼 수 있습니다.
+ trap 'kill 0' EXIT
+ sub-process-that-fails
+ rc=5
5
+ echo 5
+ exit 5
+ kill 0
0
원격 셸은 마지막 명령의 종료 코드로 종료됩니다. 예 kill 0
, 0으로 종료됩니다.