저는 스크립팅을 배우기 위해 Bash 스크립트를 작성하고 있습니다. 어떤 시점에서는 스크립트가 종료될 때 불필요한 디렉터리와 파일이 정리되도록 트랩을 추가해야 합니다. 그러나 어떤 이유로 clean_a()
스크립트가 종료되면 트랩이 정리 함수를 호출하지만 스크립트가 종료될 때 $LINENO
int 함수가 아닌 정리 함수 자체의 행을 가리킨다는 점을 이해하지 못합니다.archieve_it()
예상되는 동작:
- 스크립트 실행
- Ctrl+를 누르세요C
- 트랩 캐시 Ctrl+ C호출
clean_a()
기능 clean_a()
Ctrl이 함수는 +를 누른 행 번호를 에코합니다 C. 10번째 줄이 되도록 하세요archieve_it()
.
실제로 일어난 일:
- 스크립트 실행
- Ctrl+를 누르세요C
- 트랩 캐시 Ctrl+ C호출
clean_a()
기능 clean_a()
관련 없는 줄 번호를 에코합니다. 예를 들어clean_a()
함수의 25행입니다.
다음은 내 스크립트의 일부 예입니다.
archieve_it () {
trap 'clean_a $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
for src in ${sources}; do
mkdir -p "${dest}${today}${src}"
if [[ "$?" -ne 0 ]] ; then
error "Something!"
fi
rsync "${options}" \
--stats -i \
--log-file="${dest}${rsync_log}" \
"${excludes}" "${src}" "${dest}${today}${src}"
done
}
clean_a () {
error "something!
line: $LINENO
command: $BASH_COMMAND
removing ${dest}${today}..."
cd "${dest}"
rm -rdf "${today}"
exit "$1"
}
추신: 원본 스크립트가 표시됩니다.여기. 정의와 변수 이름은 터키어로 되어 있습니다. 필요한 경우 무엇이든 영어로 번역할 수 있습니다.
편집하다:@mikeserv의 설명을 바탕으로 스크립트를 최대한 변경했습니다. 다음과 같습니다.
#!/bin/bash
PS4='DEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
..
}
clean_a () {
error " ...
line: $LINENO $LASTNO
..."
}
이제 스크립트를 실행하고 set -x
+로 종료하면 다음과 같이 올바른 줄 번호가 인쇄됩니다.CtrlC
DDEBUG: 1 : clean_a 1 336 rsync '"${options}"' ...
그러나 clean_a()
함수에서는 값이 $LASTNO
1로 인쇄됩니다.
line: 462 1
@Arkadiusz Drabczyk가 표시한 오류와 관련이 있나요?
편집 2: @mikesrv 추천대로 스크립트를 변경했습니다. 그러나 스크립트가 종료되면 $LASTNO는 행 값으로 1을 반환합니다(337이어야 함).
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $LASTNO $LINENO
..."
} 2>&1
스크립트를 실행하고 실행 중에 +를 사용하여 rsync를 종료하면 Ctrl다음과 같은 출력이 표시됩니다.C
^^MDEBUG: 1 : clean_a '337 1 rsync "${options}" --delete-during ...
...
line: 1 465
보시다시피 $LASTNO의 값은 1입니다.
문제가 무엇인지 알아내려고 할 때 testing
매개변수 대체 형식을 사용하는 또 다른 함수를 작성했습니다 ${parameter:-default}
. 따라서 스크립트는 다음과 같습니다.
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'testing "$LASTNO $LINENO $BASH_COMMAND"'\
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
testing() {
echo -e "${1:-Unknown error!}"
exit 1
} 2>&1
이제 스크립트를 실행하고 Ctrl+를 누르면 C다음과 같은 결과가 나타납니다.
^^MDEBUG: 1 : testing '337 1 rsync "${options}" --delete-during ...
337 1 rsync "${options}" --delete-during ...
337은 rsync가 실행되는 동안 Ctrl+를 누른 줄을 가리킵니다.C
clear_a
또 다른 테스트를 위해 다음과 같은 함수를 작성해 보았습니다 .
clear_a () {
echo -e " $LASTNO $LINENO"
}
그리고 $LASTNO는 여전히 1을 반환합니다.
그러면 매개변수 대체를 사용하면 스크립트가 종료될 때 올바른 줄 번호를 얻을 수 있다는 뜻인가요?
편집 3EDIT2에서 @mikeserv의 설명을 잘못 적용한 것 같습니다. 내 실수를 바로잡았습니다. 함수의 위치 매개변수는 "$1
$LASTNO로 대체되어야 합니다.clear_a
내가 원하는 방식으로 작동하는 스크립트는 다음과 같습니다.
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $1
..."
} 2>&1
스크립트가 종료되면 - 첫 번째 매개변수 -, - 두 번째 매개변수 - 및 - 세 번째 매개변수 - 가 trap
평가되고 해당 값이 함수에 전달됩니다 . 마지막으로 스크립트가 종료되는 줄 번호로 $LASTNO를 인쇄합니다 .$LASTNO
$LINENO
$BASH_COMMAND
clear_a
$1
답변1
mikeserv의 솔루션은 좋지만 트랩을 실행하는 동안 fn
해당 라인이 전달된다는 그의 설명은 trap
올바르지 않습니다. $LINENO
앞에 줄을 삽입하면 트랩이 선언된 위치에 관계없이 트랩이 실제로 항상 전달되는 trap ...
것을 볼 수 있습니다 .fn
1
PS4='DEBUG: $LINENO : ' \
bash -x <<\EOF
echo Foo
trap 'fn "$LINENO"' EXIT
fn() { printf %s\\n "$LINENO" "$1"; }
echo "$LINENO"
exit
EOF
산출
DEBUG: 1 : echo Foo
Foo
DEBUG: 2 : trap 'fn "$LINENO"' EXIT
DEBUG: 4 : echo 4
4
DEBUG: 5 : exit
DEBUG: 1 : fn 1
DEBUG: 3 : printf '%s\n' 3 1
3
1
트랩의 첫 번째 매개변수 fn "$LINENO"
가 배치되므로하나의견적하다, $LINENO
얻다확장하다,만약에 그리고 만약에EXIT를 트리거하므로 확장해야 합니다 fn 5
. 그렇다면 왜 안 될까요? 실제로 bash-4.0까지는 트랩이 트리거될 때 $LINENO가 1로 재설정되어 fn 1
.[원천]그러나 ERR 트랩의 원래 동작은 여전히 유지됩니다. 아마도 비슷한 것이 trap 'echo "Error at line $LINENO"' ERR
너무 자주 사용되기 때문일 것입니다.
#!/bin/bash
trap 'echo "exit at line $LINENO"' EXIT
trap 'echo "error at line $LINENO"' ERR
false
exit 0
산출
error at line 5
exit at line 1
GNU bash 버전 4.3.42(1) 릴리스(x86_64-pc-linux-gnu)를 사용하여 테스트되었습니다.
답변2
내 생각에 문제는 "$LINENO"
거의 작동할 수도 있는 마지막 명령의 실행 라인을 제공할 것으로 기대하고 있다는 것입니다.clean_a()
반품자신의 것을 가지려면 $LINENO
다음과 같이 해야 합니다:
error "something!
line: $1
...
하지만 설정한 내용만 인쇄하기를 원하므로 이 방법도 작동하지 않을 수 있습니다 trap
.
다음은 작은 데모입니다.
PS4='DEBUG: $LINENO : ' \
bash -x <<\CMD
trap 'fn "$LINENO"' EXIT
fn() { printf %s\\n "$LINENO" "$1"; }
echo "$LINENO"
CMD
산출
DEBUG: 1 : trap 'fn "$LINENO"' EXIT
DEBUG: 3 : echo 3
3
DEBUG: 1 : fn 1
DEBUG: 2 : printf '%s\n' 2 1
2
1
따라서 trap
설정을 얻은 다음 fn()
정의하고 echo
실행하십시오. 쉘이 입력 실행을 마치면 EXIT
트랩이 실행되고 fn
호출됩니다. 하나의 인수가 전달됩니다. 이는 해당 trap
행의 입니다 $LINENO
. fn
먼저 자체 내용을 인쇄한 $LINENO
다음 첫 번째 인수를 인쇄합니다.
예상한 동작을 얻을 수 있는 방법을 생각할 수 있지만 이는 쉘을 엉망으로 만듭니다 stderr
.
PS4='DEBUG: $((LASTNO=$LINENO)) : ' \
bash -x <<\CMD
trap 'fn "$LINENO" "$LASTNO"' EXIT
fn() { printf %s\\n "$LINENO" "$LASTNO" "$@"; }
echo "$LINENO"
CMD
산출
DEBUG: 1 : trap 'fn "$LINENO" "$LASTNO"' EXIT
DEBUG: 3 : echo 3
3
DEBUG: 1 : fn 1 3
DEBUG: 2 : printf '%s\n' 2 1 1 3
2
1
1
3
이는 쉘의 디버그 프롬프트를 사용하여 각 실행 행을 $PS4
정의합니다 . $LASTNO
이는 현재 쉘 변수이며 스크립트의 어느 곳에서나 액세스할 수 있습니다. 즉, 현재 액세스 중인 행에 관계없이 실행 중인 스크립트의 최신 행을 참조할 수 있습니다 $LASTNO
. 물론 보시다시피 디버그 출력이 함께 제공됩니다. 이를 2>/dev/null
스크립트 실행의 대부분 으로 푸시 한 다음 다른 작업을 2>&1
수행 할 수 있습니다 clean_a()
.
1
네가 들어온 이유 $LASTNO
는예마지막 값이 설정되었습니다. $LASTNO
왜냐하면 그것이 마지막 $LINENO
값이기 때문입니다. 아래 사양에 설명된 대로 function trap
에 귀하의 기능이 있으므로 자체 archieve_it()
기능도 있습니다 . 어쨌든 올바른 일을 하고 있는 $LINENO
것처럼 보이지는 않지만 신호에 따라 셸을 다시 실행해야 하므로 재설정될 수도 있습니다. 이 경우에 나는 그것에 대해 약간 모호합니다. 분명히 그게 전부입니다.bash
trap
INT
$LINENO
bash
$LASTNO
내 생각 엔 당신이 평가를 하고 싶지 않을 것 같아요 clean_a()
. 더 나은 접근 방식은 이를 평가하고 수신된 trap
값을 매개변수로 에 전달하는 것입니다. 어쩌면 다음과 같은 것일 수도 있습니다.trap
$LASTNO
clean_a()
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
while :; do sleep 1; done
} 2>/dev/null
clean_a () { : "$@" ; } 2>&1
한번 시도해 보세요. 원하는 대로 작동해야 한다고 생각합니다. 아 - Ctrl+V Enter와 마찬가지로 문자 그대로의 반환이 PS4=^M
있다는 점에 유의하세요.^M
~에서POSIX 쉘 사양:
각 명령이 실행되기 전에 쉘에서 스크립트나 함수의 현재 연속 행 번호를 나타내는 10진수로 설정합니다(번호는 1부터 시작). 사용자가
LINENO
변수를 설정 해제하거나 재설정하면 쉘의 수명 주기 동안 특별한 의미를 잃을 수 있습니다. 쉘이 현재 스크립트나 함수를 실행하고 있지 않으면 값이LINENO
지정되지 않습니다. IEEE Std 1003.1-2001의 이 볼륨은 사용자 이식성 유틸리티 옵션을 지원하는 시스템에 대한 변수의 효과만 지정합니다.
답변3
LINENO = 0
스크립트가 종료될 때 실제 줄 번호 대신 을 얻는 것은 ERR
(대신 ) 캡처하여 EXIT
수정할 수 있습니다.
또한 set -E
이러한 ERR 트랩이 함수, 명령 대체 및 하위 쉘 환경에 의해 상속되도록 하기 위해 추가되었습니다. 예를 들어, 스크립트 오류를 일으킨 함수 이름과 해당 줄 번호를 인쇄하는 방법은 다음과 같습니다.
set -eE
trap 'echo "Error in function $FUNCNAME at line $LINENO"' ERR
감사의 말씀:https://citizen428.net/blog/bash-error-handling-with-trap/
답변4
그래서 우연히 bash에서 $LINENO 변수를 올바르게 표시하고(예를 들어 DEBUG 또는 EXIT 트랩에서) 예상하는 줄 번호를 제공하는 방법을 발견했습니다. 다른 공개된 방법보다 훨씬 간단하며 PS4 및 STDERR을 하이재킹할 필요가 없습니다. 잘 모르겠어요왜이것은 효과가 있지만 다른 사람이 설명할 수 있다면 그 이유를 알고 싶습니다.
기본적으로 실행하려는 항목을 더미 함수(예: 호출 _ff
) 로 래핑하고 source <(declare -f _ff)
실행하면 _ff
in의 모든 인스턴스에 실제로 줄 번호가 부여됩니다.$LINENO
_ff
예
_ff() (
trap 'echo "LINENO=$LINENO"' DEBUG;
echo a
echo b
echo c
echo d
echo e
echo f
echo g
)
source <(declare -f _ff)
이제 실행하면 _ff
출력됩니다.
LINENO=2
a
LINENO=3
b
LINENO=4
c
LINENO=5
d
LINENO=6
e
LINENO=7
f
LINENO=8
g
참고: 달리기가 source <(declare -f _ff)
핵심입니다. 달리지 않으면 LINENO=1
모든 $LINENO
명령문을 얻게 됩니다.
참고 사항: 이 질문은 꽤 오래되었지만 기본적으로 ERR 트랩 외부에 올바른 $LINENO를 표시하기 위한 작동하는 솔루션을 제공하는 인터넷에서 찾은 유일한 장소입니다. 그래서 여기에 게시하는 것이 적절할 것 같습니다.