![평가: $? 및 ${PIPESTATUS[@]}(bash)](https://linux55.com/image/225497/%ED%8F%89%EA%B0%80%3A%20%24%3F%20%EB%B0%8F%20%24%7BPIPESTATUS%5B%40%5D%7D(bash).png)
Bash 5.0에서는 캡처가 ${PIPESTATUS[@]}
통과되기를 원합니다 eval
. 그러나 마스크는 마스크 와 동일 하지 않은 eval
것 같습니다 . 결과에서 추출하는 방법이 있나요 ? 아래 명령 문자열에 무언가를 추가해도 작동하지 않는 것 같습니다.${PIPESTATUS[@]}
$?
${PIPESTATUS[-1]}
${PIPESTATUS[@]}
eval
&& array=( ${PIPESTATUS[@]} ) && export array
$?
이것이 간단하지 않다고 가정하는 것이 맞습니까 ${PIPESTATUS[-1]}
?
내 샘플 코드(루트로 실행):
#!/usr/bin/env bash
#without eval, ${PIPESTATUS[@]} has two entries as it should.
apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
echo ""
echo ""
#with eval, ${PIPESTATUS[@]} has one entry and is equal to $?
commandstring="apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log"
eval "$commandstring"
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
편집: PIPESTATUS에 대한 원래 설명에서 약간의 기술적 수정 사항을 수정했습니다. 또한 아래 답변을 바탕으로 몇 가지 설명이 있습니다.
- 프로그래밍 방식으로 명령 문자열을 작성하고 있기 때문에 이것을 사용하고 있습니다
eval
. 그 중 일부에는bash -c
... 다른 사용자로 명령을 실행하세요. - 설정을 살펴본
pipefail
결과 때로는 작동할 수도 있지만 항상 작동하지는 않습니다. 파이프라인의 여러 단계 상태를 알아야 하는 경우도 있기 때문입니다. 설정set -o pipefail
한 다음 설정을 해제할 수 있으면 작동하지만 설정을 해제하는 방법을 모르고 단순히 서브셸을 종료하고 설정되지 않은 새 서브셸pipefail
로 계속 진행할 수 없습니다 .pipefail
예를 들어 쉘 옵션을 설정 해제하는 방법은 무엇입니까pipefail
? - 포함된 배열을 내보내는 방법에 대한 위의 이해가 잘못 되었습니까
PIPESTATUS
? 서브셸PIPESTATUS
에서 어떻게 간단히 내보낼 수 있나요 ?eval
편집 2: 아래 표시된 훌륭한 답변 덕분에 최종 결정은 리디렉션이 포함된 명령 문자열을 동적으로 조합하는 상황(평가가 필요한 이유)을 사용 set -o pipefail
하고 실행 후에 수행한다는 것이었습니다 set +o pipefail
.
또한 지난번 이후로 몇 년간의 bash 경험이 있기 때문에 동적 빌드 명령을 실행하는 모범 사례를 다시 연구하고 있습니다. 내 사용 사례 eval
는 다음과 같습니다
- 명령에 가 포함될 수 있으므로 이를 실행하는
bash -c
데 사용할 수 없습니다 .bash -c
- 리디렉션이 포함될 수 있습니다.
>> log
- 동적 명령은 디버깅 목적으로 매개변수를 추가할 수도 있습니다.
내가 아는 한, 1에서는 다른 작업을 수행할 수 있고 3에서는 set -x [command]
및 과 같은 trap
것을 사용해야 합니다. 2에 대한 해결책은 아직 찾지 못했습니다.
답변1
$?
마지막 명령 실행의 가장 오른쪽 구성 요소(또는 설정된 경우 가장 오른쪽 실패한 구성 요소)의 상태를 포함합니다 pipefail
. 단, 이는 !
prefix 키워드로 수정될 수 있습니다.
요소는 $PIPESTATUS
이전 파이프라인의 각 구성요소의 종료 상태이며 !
값에 영향을 주지 않습니다.
그 이후에는 (exit 1) | (exit 2) | (exit 3)
( $PIPESTATUS
1 2 3)이 되고 $?
3이 됩니다. 그 이후에는 ! (exit 1) | (exit 2) | (exit 3)
동일 $PIPESTATUS
하지만 $?
0이 되어 와 같습니다 $(( ! 3 ))
.
false
또는 (exit 1)
여전히 하나의 구성 요소가 있는 파이프입니다. 첫 번째 경우에는 간단한 명령이고 두 번째 경우에는 하위 쉘이므로 PIPESTATUS=(1)
and $?
= 1을 얻습니다.
이것은 똑같습니다. eval 'any shell code'
단지 간단한 명령일 뿐입니다.
그 다음에 eval 'A | B' | C
는 $PIPESTATUS
종료 상태 eval
(마지막으로 실행한 명령의 종료 상태) B
와 종료 상태가 포함됩니다 C
. (A | B) | C
그건 그렇고, 마찬가지입니다.
다음을 수행할 수 있습니다.
eval '
A | B
saved_STATUS=$? saved_PIPESTATUS=( "${PIPESTATUS[@]}" )
'
호출하는 인터프리터에서 볼 수 있듯이 파이프라인 구성 요소의 상태를 유지하고 싶으므로 eval
귀하의 경우에는 다음을 수행하십시오.
preserve_status='
saved_STATUS=$? saved_PIPESTATUS=( "${PIPESTATUS[@]}" )
'
eval "$commandstring $preserve_status"
commandsPipestatus=( "${saved_PIPESTATUS[@]}" )
for status in "${commandsPipestatus[@]}"; do
echo "$status"
done
그러나 여기서는 다음을 원하는 것 같습니다.
install_java() (
set -o pipefail
apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log
)
그건:
- 변수가 아닌 함수에 코드 저장
- 함수가 실패 하거나 실패할 경우 실패를 반환
pipefail
하려면 이 옵션을 사용하십시오 . 함수 본문은 더 일반적인 서브쉘이 아니라 서브쉘입니다.apt-get
tee
명령 그룹command({ ...; }
)는 옵션이 로컬에서만 설정되었음을 나타냅니다. 최신 버전에서는 서브셸 없이도 함수에 로컬로 설정된 옵션을bash
사용할 수도 있습니다 .local -; set -o pipefail
그러나 일반 출력과 오류는 모두 apt-get
stdout으로 전송됩니다.
을 사용하는 경우 zsh
다음을 수행할 수 있습니다.
exec {log}>> ~/log # at the beginning of the script for instance.
apt-get install -y java-17-openjdk-amd64 >&1 >&$log 2>&2 2>&$log
stderr의 stdout을 apt-get
로그와 해당 원래 대상으로 보냅니다. 내부적으로 ing이 완료되고 tee
종료 상태가 유지됩니다.apt-get
다음과 같이 이를 위한 도우미 함수를 만들 수도 있습니다.
#! /bin/zsh -
die() { print -ru2 -C1 -- "$@"; exit 1; }
exec {log}>> ~/log
with_log() { "$@" >&1 >&$log 2>&2 2>&$log; }
with_log apt-get ... || die "Aborting..."
답변2
대신 함수를 사용하는 것이 eval
더 나은 솔루션일 수 있습니다.
따라서 필요한 경우 여러 패키지를 설치할 수 있도록 허용하면 됩니다.
install() {
apt-get install -y "${1}" 2>&1 | tee -a ~/log
}
packages=("java-17-openjdk-amd64")
for pkg in "${packages[@]}" ; do
install pkg
# replace below with actions based on status
printf "%s\n" "${PIPESTATUS[@]}"
done
set -o pipefail
apt-get
명령을 모두 확인하지 않고 오류를 제거하는 방법이지만 PIPESTATUS
로그에 쓸 수 없으면 상황이 약간 흐려질 수 있습니다.tee
편집: 추가 정보를 바탕으로
나는 당신이 겪고 있는 문제가 eval
서브쉘(또는 적어도 그와 유사한 것)에서 명령을 실행하기 때문이라고 생각합니다. 종료 코드만 받는 것처럼 느껴집니다(가정이 틀려서 다행입니다).
명령에서 파이프 상태를 인쇄할 수 있지만 가져오는 것이 까다로울 수 있습니다.
이 괴물은 처리할 수 있는 한 가지 방법이지만 매우 복잡하고 텍스트 처리 측면에서 명령을 일부 사용자 정의해야 하며 결국에는 종료 코드만 받게 됩니다.
# just some stuff to print some text and exit with a non zero
# before the next pipeline step masks the exit code
foo='bash -c "echo hello;false" | tee log;echo PIPE ${PIPESTATUS[@]}'
# awk grabs the line with the pipe status text, pulls one of
# the exit codes from it and exits with it, otherwise prints
# the output as seen
eval "$foo" | awk '
{if ($1=="PIPE"){print "just showing you I saw all the codes "$0;exit $2}; print $0}'
echo $?
awk
다양한 파이프라인 상태를 캡처 하기 위해 출력을 제공하기 위해 뭔가를 할 수 있을 것 같지만 readarray
, 그러면 매우 지저분해질 것입니다(이미 지저분한 상태임에도 불구하고).
상황이 이렇게 추악해지면 전체적인 접근 방식이 잘못된 것인지 궁금해지기 시작해야 합니다.
어쩌면 요구 사항을 더 자세히 설명하면 이를 달성하는 다른 방법이 나타날 수도 있습니다.