SSH를 통해 실행된 bash 스크립트가 잘못된 종료 코드 0을 반환합니다.

SSH를 통해 실행된 bash 스크립트가 잘못된 종료 코드 0을 반환합니다.

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':

  1. 로컬 쉘은 작은따옴표(견적 삭제);
  2. 클라이언트는 ssh매개변수 host, sh-c을 가져옵니다 exit 5. 이를 문자열로 연결하여 원격 호스트로 보냅니다.
  3. 원격 호스트에서 ssh셸을 호출하고 문자열을 전달합니다 sh -c exit 5.
  4. 아래와 같이 원격 셸이 호출되고 옵션이 sh전달됩니다 .-cexit명령 문자열, 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옵션으로 실행됩니다. 두 번째와 세 번째 테스트의 차이점은 전체 명령 및 매개변수 세트가 단일 명령 매개변수로 참조되는 원격 호스트로 전송된다는 것입니다.-cshssh

답변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알았습니다 . .bashrcBash 추적에는 이 트랩의 실행이 표시되지 않는 것 같습니다. 트랩을 래퍼 스크립트로 옮겼습니다. 이제 실제로 실행되는 내용을 볼 수 있습니다.

+ trap 'kill 0' EXIT
+ sub-process-that-fails
+ rc=5
5
+ echo 5
+ exit 5
+ kill 0
0

원격 셸은 마지막 명령의 종료 코드로 종료됩니다. 예 kill 0, 0으로 종료됩니다.

관련 정보