웹사이트 검색

Linux에서 Bash 스크립트의 오류를 잡는 방법


기본적으로 Linux의 Bash 스크립트는 오류를 보고하지만 계속 실행됩니다. 다음에 발생해야 할 일을 결정할 수 있도록 오류를 직접 처리하는 방법을 보여줍니다.

스크립트의 오류 처리

오류 처리는 프로그래밍의 일부입니다. 완벽한 코드를 작성하더라도 여전히 오류 상태에 빠질 수 있습니다. 컴퓨터의 환경은 소프트웨어를 설치 및 제거하고, 디렉토리를 만들고, 업그레이드 및 업데이트를 수행하면서 시간이 지남에 따라 변경됩니다.

예를 들어 문제 없이 실행되던 스크립트는 디렉터리 경로가 변경되거나 파일에 대한 권한이 변경되면 문제가 발생할 수 있습니다. Bash 셸의 기본 작업은 오류 메시지를 인쇄하고 스크립트를 계속 실행하는 것입니다. 이것은 위험한 불이행입니다.

실패한 작업이 스크립트에서 나중에 발생하는 다른 처리 또는 작업에 중요한 경우 해당 중요한 작업은 성공하지 못합니다. 결과가 얼마나 비참한지는 스크립트가 수행하려는 작업에 따라 다릅니다.

보다 강력한 체계는 오류를 감지하고 종료해야 하거나 결함 조건을 해결해야 하는 경우 스크립트가 작동하도록 합니다. 예를 들어 디렉토리나 파일이 누락된 경우 스크립트가 이를 다시 생성하는 것이 만족스러울 수 있습니다.

스크립트에서 복구할 수 없는 문제가 발생하면 종료될 수 있습니다. 스크립트를 종료해야 하는 경우 임시 파일을 제거하거나 오류 조건 및 종료 이유를 로그 파일에 쓰는 등 필요한 모든 정리 작업을 수행할 수 있습니다.

종료 상태 감지

명령 및 프로그램은 종료 시 운영 체제로 전송되는 값을 생성합니다. 이를 종료 상태라고 합니다. 오류가 없는 경우 값이 0이고 오류가 발생한 경우 0이 아닌 값이 있습니다.

스크립트가 사용하는 명령의 종료 상태(반환 코드라고도 함)를 확인하고 명령의 성공 여부를 확인할 수 있습니다.

Bash에서 0은 true와 같습니다. 명령의 응답이 사실이 아닌 경우 문제가 발생한 것으로 알고 적절한 조치를 취할 수 있습니다.

이 스크립트를 편집기에 복사하고 bad_command.sh라는 파일에 저장합니다.

#!/bin/bash

if ( ! bad_command ); then
  echo "bad_command flagged an error."
  exit 1
fi

chmod 명령으로 스크립트를 실행 가능하게 만들어야 합니다. 이것은 스크립트를 실행 가능하게 만드는 데 필요한 단계이므로 자신의 컴퓨터에서 스크립트를 시도하려면 각 스크립트에 대해 이 작업을 수행해야 합니다. 각 경우에 적절한 스크립트의 이름을 대체하십시오.

chmod +x bad_command.sh

스크립트를 실행하면 예상되는 오류 메시지가 표시됩니다.

./bad_command.sh

bad_command와 같은 명령은 없으며 스크립트 내의 함수 이름도 아닙니다. 실행할 수 없으므로 응답은 0이 아닙니다. 응답이 0이 아닌 경우(여기서는 느낌표가 논리 NOT 연산자로 사용됨) if 문의 본문이 실행됩니다.

실제 스크립트에서는 이 예제에서와 같이 스크립트를 종료하거나 결함 조건을 해결하려고 시도할 수 있습니다.

exit 1 줄이 중복된 것처럼 보일 수 있습니다. 결국 스크립트에는 다른 것이 없으며 어쨌든 종료됩니다. 그러나 exit 명령을 사용하면 종료 상태를 다시 셸로 전달할 수 있습니다. 스크립트가 두 번째 스크립트 내에서 호출되면 두 번째 스크립트는 이 스크립트에 오류가 발생했음을 알게 됩니다.

명령의 종료 상태와 함께 논리 OR 연산자를 사용할 수 있으며 첫 번째 명령에서 0이 아닌 응답이 있는 경우 스크립트에서 다른 명령이나 함수를 호출할 수 있습니다.

command_1 || command_2

이것은 첫 번째 명령이 OR 두 번째 명령을 실행하기 때문에 작동합니다. 맨 왼쪽 명령이 먼저 실행됩니다. 성공하면 두 번째 명령이 실행되지 않습니다. 그러나 첫 번째 명령이 실패하면 두 번째 명령이 실행됩니다. 따라서 우리는 이와 같은 코드를 구성할 수 있습니다. 이것은 logical-or./sh입니다.

#!/bin/bash

error_handler()
{
  echo "Error: ($?) $1"
  exit 1
}

bad_command || error_handler "bad_command failed, Line: ${LINENO}"

우리는 error_handler라는 함수를 정의했습니다. 이렇게 하면 $? 변수에 저장된 실패한 명령의 종료 상태와 함수가 호출될 때 전달되는 텍스트 줄이 출력됩니다. 이것은 $1 변수에 보관됩니다. 함수는 종료 상태 1로 스크립트를 종료합니다.

스크립트는 분명히 실패하는 bad_command를 실행하려고 시도하므로 논리 OR 연산자의 오른쪽에 있는 명령인 ||가 실행됩니다. 이것은 error_handler 함수를 호출하고 실패한 명령의 이름을 지정하고 실패한 명령의 줄 번호를 포함하는 문자열을 전달합니다.

스크립트를 실행하여 오류 처리기 메시지를 확인한 다음 echo를 사용하여 스크립트의 종료 상태를 확인합니다.

./logical-or.sh
echo $?

작은 error_handler 함수는 bad_command 실행 시도의 종료 상태, 명령 이름 및 줄 번호를 제공합니다. 이것은 스크립트를 디버깅할 때 유용한 정보입니다.

스크립트의 종료 상태는 1입니다. error_handler에서 보고한 127 종료 상태는 명령을 찾을 수 없음을 의미합니다. 원하는 경우 이를 exit 명령에 전달하여 스크립트의 종료 상태로 사용할 수 있습니다.

또 다른 접근 방식은 error_handler를 확장하여 종료 상태의 다른 가능한 값을 확인하고 그에 따라 다른 작업을 수행하는 것입니다.

exit_code=$?

if [ $exit_code -eq 1 ]; then
  echo "Operation not permitted"

elif [ $exit_code -eq 2 ]; then
  echo "Misuse of shell builtins"
.
.
.
elif [ $status -eq 128 ]; then
  echo "Invalid argument"
fi

set을 사용하여 강제 종료

오류가 발생할 때마다 스크립트를 종료하고 싶다면 강제로 종료할 수 있습니다. 오류를 감지하는 즉시 스크립트가 종료되기 때문에 정리 또는 추가 손상의 기회를 포기한다는 의미입니다.

이렇게 하려면 -e(오류) 옵션과 함께 set 명령을 사용하십시오. 이것은 명령이 실패하거나 0보다 큰 종료 코드를 반환할 때마다 종료하도록 스크립트에 지시합니다. 또한 -E 옵션을 사용하면 셸 함수에서 오류 감지 및 트래핑이 작동합니다.

초기화되지 않은 변수도 포착하려면 -u(설정되지 않은) 옵션을 추가하십시오. 파이프 시퀀스에서 오류가 감지되도록 하려면 -o pipefail 옵션을 추가하십시오. 이것이 없으면 파이프로 연결된 명령 시퀀스의 종료 상태는 시퀀스에서 최종 명령의 종료 상태입니다. 파이프 연결 시퀀스 중간에 실패한 명령은 감지되지 않습니다. -o pipefail 옵션은 옵션 목록에 있어야 합니다.

스크립트 상단에 추가할 순서는 다음과 같습니다.

set -Eeuo pipefail

다음은 설정되지 않은 변수가 포함된 unset-var.sh라는 짧은 스크립트입니다.

#!/bin/bash

set -Eeou pipefail

echo "$unset_variable"

echo "Do we see this line?"

스크립트를 실행하면 unset_variable이 초기화되지 않은 변수로 인식되고 스크립트가 종료됩니다.

./unset-var.sh

두 번째 echo 명령은 실행되지 않습니다.

오류가 있는 트랩 사용

Bash 트랩 명령을 사용하면 특정 신호가 발생할 때 호출해야 하는 명령이나 함수를 지정할 수 있습니다. 일반적으로 Ctrl+C 키 조합을 누를 때 발생하는 SIGINT와 같은 신호를 포착하는 데 사용됩니다. 이 스크립트는 sigint.sh입니다.

#!/bin/bash

trap "echo -e '\nTerminated by Ctrl+c'; exit" SIGINT

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

trap 명령에는 echo 명령과 exit 명령이 포함되어 있습니다. SIGINT가 발생하면 트리거됩니다. 나머지 스크립트는 간단한 루프입니다. 스크립트를 실행하고 Ctrl+C를 누르면 트랩 정의의 메시지가 표시되고 스크립트가 종료됩니다.

./sigint.sh

ERR 신호와 함께 trap을 사용하여 발생하는 오류를 포착할 수 있습니다. 그런 다음 명령이나 기능에 공급할 수 있습니다. 이것은 trap.sh입니다. error_handler라는 함수에 오류 알림을 보내고 있습니다.

#!/bin/bash

trap 'error_handler $? $LINENO' ERR

error_handler() {
  echo "Error: ($1) occurred on $2"
}

main() {
  echo "Inside main() function"
  bad_command
  second
  third
  exit $?
}

second() {
  echo "After call to main()"
  echo "Inside second() function"
}

third() {
  echo "Inside third() function"
}

main

스크립트의 대부분은 secondthird 함수를 호출하는 main 함수 안에 있습니다. 오류가 발생하면(이 경우 bad_command가 존재하지 않기 때문에) trap 문이 오류를 error_handler 함수로 보냅니다. 실패한 명령의 종료 상태와 줄 번호를 error_handler 함수에 전달합니다.

./trap.sh

우리의 error_handler 함수는 단순히 터미널 창에 대한 오류의 세부 정보를 나열합니다. 원하는 경우 함수에 exit 명령을 추가하여 스크립트를 종료할 수 있습니다. 또는 일련의 if/elif/fi 문을 사용하여 다양한 오류에 대해 다양한 작업을 수행할 수 있습니다.

일부 오류는 수정할 수 있고 다른 오류는 스크립트를 중지해야 할 수도 있습니다.

마지막 팁

오류를 포착한다는 것은 종종 잘못될 수 있는 일을 사전에 비우고 이러한 상황이 발생할 경우 이를 처리하기 위해 코드를 넣는 것을 의미합니다. 이는 스크립트의 실행 흐름과 내부 논리가 올바른지 확인하는 것 외에도 추가됩니다.

이 명령을 사용하여 스크립트를 실행하면 Bash는 스크립트가 실행될 때 추적 출력을 표시합니다.

bash -x your-script.sh

Bash는 터미널 창에 추적 출력을 씁니다. 인수가 있는 경우 인수와 함께 각 명령어를 표시합니다. 이는 명령이 확장된 후 실행되기 전에 발생합니다.

파악하기 어려운 버그를 추적하는 데 큰 도움이 될 수 있습니다.