"bash -x"가 이 스크립트를 중단시키는 이유는 무엇입니까?

"bash -x"가 이 스크립트를 중단시키는 이유는 무엇입니까?

특정 명령을 실행하는 데 걸리는 시간을 측정하는 스크립트가 있습니다.

time"실제" 명령, 즉 바이너리가 필요합니다 /usr/bin/time(bash 내장에는 해당 플래그가 없기 때문입니다 -f).

디버깅할 수 있는 단순화된 스크립트는 다음과 같습니다.

#!/bin/bash

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

echo ABC--$TIMESEC--DEF

if [ "$TIMESEC" -eq 0 ] ; then
   echo "we are here!"
fi

"test.sh"로 저장하고 다음을 실행합니다.

$ bash test.sh
ABC--0--DEF
we are here!

그래서 작동합니다.

이제 bash 명령줄에 "-x"를 추가하여 디버깅해 보겠습니다.

$ bash -x test.sh
++ echo blah
++ awk -F. '{print $1}'
+ TIMESEC='++ /usr/bin/time -f %e grep blah
0'
+ echo ABC--++ /usr/bin/time -f %e grep blah 0--DEF
ABC--++ /usr/bin/time -f %e grep blah 0--DEF
+ '[' '++ /usr/bin/time -f %e grep blah
0' -eq 0 ']'
test.sh: line 10: [: ++ /usr/bin/time -f %e grep blah
0: integer expression expected

"-x"를 사용하면 이 스크립트가 중단되지만, "-x" 없이는 잘 작동하는 이유는 무엇입니까?

답변1

문제는 다음 줄입니다.

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

표준 출력과 일치하도록 표준 오류를 리디렉션하고 있습니다. bash는 표준 오류에 대한 추적 메시지를 작성하고 (예를 들어) bash 프로세스 내의 내장 함수 echo및 기타 쉘 구성을 사용합니다.

다음과 같은 것으로 바꾸면

TIMESEC=$(echo blah | sh -c "( /usr/bin/time -f %e grep blah >/dev/null )" 2>&1 | awk -F. '{print $1}')

이는 이 문제를 해결하고 추적과 ​​작업 사이에서 허용 가능한 절충안이 될 수 있습니다.

++ awk -F. '{print $1}'
++ sh -c '( /usr/bin/time -f %e grep blah >/dev/null )'
++ echo blah
+ TIMESEC=0                 
+ echo ABC--0--DEF
ABC--0--DEF
+ '[' 0 -eq 0 ']'
+ echo 'we are here!'
we are here!

답변2

서브쉘을 직접 삭제할 수도 있습니다. 분명히 서로 화나게 되는 것은 중첩된 껍질입니다.

TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null |
    awk -F. '{print $1}'
)

이렇게 하면:


...| ( subshell ) 2>pipe | ...

...파이프라인의 해당 부분을 처리하기 위해 서브셸을 실행하게 됩니다.호스팅내부의 서브쉘입니다. 쉘은 서브쉘의 디버그 출력을 리디렉션하지도 않기 때문에( 사용하기로 선택할 수 있는 다른 종류의 {복합 명령 에서도 작동합니다); } >redirect 파이프 섹션에서 흐름을 혼합합니다. 리디렉션 순서와 관련이 있습니다.

대신 먼저 리디렉션하면오직측정하려는 명령의 오류 출력을 갖고 호스트 셸의 출력이 이를 stderr로 보내도록 하면 동일한 문제가 발생하지 않습니다.

그래서...


... | command 2>pipe 1>/dev/null | ...

...호스트 셸은 원하는 곳 어디에서나 stderr에 자유롭게 계속 쓸 수 있으며 호출하는 명령의 출력만 파이프로 리디렉션합니다.


bash -x time.sh
+++ echo blah
+++ /usr/bin/time -f %e grep blah
+++ awk -F. '{print $1}'
++ TIMESEC=0
++ echo ABC--0--DEF
ABC--0--DEF
++ '[' 0 -eq 0 ']'
++ echo 'we are here!'
we are here!

이 질문에 대해서...


TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null
)
printf %s\\n "ABC--${TIMESEC%%.*}--DEF"
if [ "${TIMESEC%%.*}" -eq 0 ] ; then
   echo "we are here!"
fi

관련 정보