
heredoc에서 제공되는 비대화형 명령을 실행하는 경우(어쩌면 파일에서도 테스트하지 않았음) 및 a read
(또는 유사) heredoc의 명령을 사용하면 read
명령 뒤의 코드가 다음과 같이 됩니다.무시됨, 실행이 종료됩니다.오류 없이, 명령 실행~ 후에heredoc을 실행하는 명령입니다.
좀 더 구체적으로 말하자면, 여러 Linux 유틸리티의 도구 상자 역할을 하는 도커 컨테이너에서 코드를 실행하고 있는데, 이 문제로 인해 코드가 예상대로 일어날지 믿을 수 없다는 점에서 실제로 안전하지 않게 됩니다.
문제의 샘플은 다음과 같습니다.
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
read 'var'
echo "after inside" >&2
SHELL
echo "after outside" >&2
read
위의 코드는 모든 에코를 실행하고 성공적으로 끝나거나 명령에 오류가 발생하고 오류로 끝날 것으로 예상했습니다 .
불행히도 위 코드의 출력은 다음과 같습니다.
before outside
before inside
after outside
read
기본적으로 명령 뒤의 내용을 무시했습니다.read
, 아마도 명령에 의사 tty를 할당하지 않는 명령이 있기 때문일 수 있습니다 ( docker run -it
의사 tty를 할당하지만 명령에 파이프되는 heredoc 때문에 위 코드를 실행할 수 없습니다. 대신에 다음을 제공합니다. 오류: the input device is not a TTY
).
2를 제거하면 을 set -eou pipefail
사용해도 동일한 문제가 발생하므로 read 'var' ||:
(중 하나와 관련이 없다고 생각합니다.문제의) set -e
.
예상되는 결과의 예는 다음과 같습니다.
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
unknow_command error ||:
echo "after inside" >&2
SHELL
echo "after outside" >&2
성공적으로 끝나고 다음과 같이 인쇄됩니다.
before outside
before inside
/bin/bash: line 3: unknow_command: command not found
after inside
after outside
또는:
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
unknow_command error
echo "after inside" >&2
SHELL
echo "after outside" >&2
오류로 끝나고 다음이 인쇄됩니다.
before outside
before inside
/bin/bash: line 3: unknow_command: command not found
read
나에게는 명령을 건너 뛰고 (예를 들어 빈 값을 입력으로 사용) 이후의 모든 명령이 실행되거나 heredoc에 있는 명령이라도 실행되거나 오류가 발생하고 실행이 즉시 중지되면 괜찮을 것입니다 . 나는 docker 명령의 오류를 무시하지 않습니다).
read
위의 내용은 단지 예일 뿐이며 실제 사례에서는 (또는 유사한) 명령이 직접 호출되지 않고 명령 내에서 일부 조건에서만 호출될 수 있기 때문에 훨씬 더 나쁠 수 있습니다 . 예를 들어:
#!/bin/bash
set -eou pipefail
docker run --rm -i my_mysql /bin/bash <<-SHELL
set -eou pipefail
some_important_command_1
mysql -u "$user" -p "$pass" -e "some sql command"
some_important_command_2
SHELL
some_important_command_after_2
위의 코드는 괜찮아 보일 수 있지만 암호가 비어 있으면 stdin에서 읽기를 시도하여 첫 번째 예에서 문제를 일으킵니다. 건너뛰지만 some_important_command_2
실행하면 some_important_command_after_2
이후에만 실행되어야 합니다 some_important_command_2
.
위의 mysql 예제도 단지 예제일 뿐이므로 이 경우 비밀번호가 비어 있는지 확인하고 처리할 수 있습니다.진짜 문제는 이 문제가 코드 내에서 발생할 수 있는지 판단할 수 없고 이를 피할 수 있는 안전한 방법도 없다는 것입니다., docker 도구 상자 컨테이너의 사용을 중지하고 모든 호스트 내에 모든 유틸리티 및 기타 항목을 설치하고 최신 상태로 유지하는 것(컨테이너 이미지를 최신 상태로 유지하는 대신)을 제외하고. 또한 컨테이너 내부의 서비스에서 구체적으로 실행되는 명령(위의 mysql 예와 같은)에는 작동하지 않습니다.
누군가 위의 문제에 대한 해결책을 가지고 있습니까?솔루션은구체적이지 않다제가 제시한 예에는 문제가 없지만 이를 해결할 수 있는 일반적인 접근 방식은 다음과 같습니다.이런 종류의 버그(성공적으로 완료하거나, 모든 명령을 실행하거나, 오류를 발생시키고 모든 후속 명령을 중지함으로써)
업데이트:
declare -p var
출력 후 추가 echo "after inside"
:
before outside
before inside
declare -- var="echo \"after inside\" >&2"
after outside
나는 @muru가 주석에서 지적한 대로 read 명령이 heredoc에서 읽는 것으로 끝난다고 생각합니다. echo "var=\$var"
뒤에 추가하면 볼 수도 있습니다 . 그러면 echo "after inside"
다음이 인쇄됩니다 var=echo "after inside" >&2
. 이제 heredoc 자체에서 해당 읽기를 건너뛰는 방법을 찾아야 합니다.