Linux bash 스크립트에서 오류를 포착하는 방법은 무엇입니까?

다음 스크립트를 만들었습니다.

# !/bin/bash # OUTPUT-COLORING red="\e[0;31m" green="\e[0;32m" NC="\e[0m" # No Color # FUNCTIONS # directoryExists - Does the directory exist? function directoryExists { cd $1 if [ $? = 0 ] then echo -e "${green}$1${NC}" else echo -e "${red}$1${NC}" fi } # EXE directoryExists "~/foobar" directoryExists "/www/html/drupal" 

스크립트는 작동하지만 내 에코,

cd $1 

실행 실패시 출력도 있습니다.

testscripts//test_labo3: line 11: cd: ~/foobar: No such file or directory 

이것을 잡을 수 있습니까?

댓글

  • 참고로이 작업을 훨씬 더 간단하게 수행 할 수도 있습니다. test -d /path/to/directory (또는 bash의 [[ -d /path/to/directory ]])는 주어진 대상이 디렉토리인지 아닌지를 알려주며 조용히 수행합니다.
  • @Patrick, ‘ 디렉토리에 cd 할 수 있는지가 아닌 디렉토리인지 테스트합니다.

li>

  • @StephaneChazelas 그렇습니다. 함수 이름은 directoryExists입니다.
  • 여기에서 자세한 답변을 확인하세요. Bash 스크립트에서 오류 발생 a>.
  • 답변

    스크립트가 실행되면 디렉토리가 변경됩니다. 즉, 일련의 상대 경로 이름. 그런 다음 나중에 cd를 사용할 수있는 기능이 아니라 디렉토리 존재 만 확인하고 싶다고 주석을 달았으므로 답변은 cd입니다. 개정. tputman terminfo의 색상 사용 :

    #!/bin/bash -u # OUTPUT-COLORING red=$( tput setaf 1 ) green=$( tput setaf 2 ) NC=$( tput setaf 0 ) # or perhaps: tput sgr0 # FUNCTIONS # directoryExists - Does the directory exist? function directoryExists { # was: do the cd in a sub-shell so it doesn"t change our own PWD # was: if errmsg=$( cd -- "$1" 2>&1 ) ; then if [ -d "$1" ] ; then # was: echo "${green}$1${NC}" printf "%s\n" "${green}$1${NC}" else # was: echo "${red}$1${NC}" printf "%s\n" "${red}$1${NC}" # was: optional: printf "%s\n" "${red}$1 -- $errmsg${NC}" fi } 

    (편집 됨 텍스트의 이스케이프 시퀀스에 영향을 미칠 수있는 문제가있는 echo 대신 더 무적의 printf를 사용합니다.)

    댓글

    • 파일 이름에 백 슬래시 문자가 포함 된 문제도 수정합니다 (xpg_echo가 켜져 있지 않은 경우).

    답변

    set -e를 사용하여 오류 발생시 종료 모드를 설정합니다. 간단한 명령이 0이 아닌 상태 (실패를 나타냄)를 반환하는 경우 쉘이 종료됩니다.

    set -e가 항상 시작되는 것은 아닙니다. 테스트 위치의 명령은 실패 할 수 있습니다 (예 : if failing_command, failing_command || fallback). 하위 셸의 명령은 상위가 아닌 하위 셸만 종료합니다. set -e; (false); echo foofoo.

    또는 bash (및 ksh 및 zsh (일반 sh는 아님), “명령이 0이 아닌 상태를 반환하는 경우 실행되는 명령을 ERR 트랩과 함께 지정할 수 있습니다 (예 : trap "err=$?; echo >&2 "Exiting on error $err"; exit $err" ERR (false); …와 같은 경우에는 ERR 트랩이 서브 쉘에서 실행되므로 부모를 종료 할 수 없습니다.

    코멘트

    • 최근 약간의 실험을 통해 트랩을 사용하지 않고 적절한 오류 처리를 쉽게 수행 할 수있는 || 동작을 수정하는 편리한 방법을 발견했습니다. 내 대답 . 그 방법에 대해 어떻게 생각하십니까?
    • @ sam.kozin 저는 동의합니다 ‘ 답변을 자세히 검토 할 시간이 없습니다. 원칙적으로보기 좋습니다. 이식성 외에도 ksh / bash / zsh ‘의 ERR 트랩에 비해 어떤 이점이 있습니까?

    li>

  • 유일한 이점은 구성 가능성 일 것입니다. ‘ 함수가 실행되기 전에 설정된 다른 트랩을 덮어 쓸 위험이 없기 때문입니다. 이는 유용한 기능입니다. ‘ 나중에 다른 스크립트에서 소싱하고 사용할 공통 함수를 작성하고 있습니다. 또 다른 이점 모든 주요 셸에서 ERR 의사 신호가 지원되므로 그렇게 중요하지는 않지만 완전한 POSIX 호환성 일 수 있습니다. 검토해 주셔서 감사합니다! =)
  • @ sam.kozin 이전 댓글에 쓰는 것을 잊었습니다.이 글을 코드 리뷰 에 게시하고 채팅룸 의 링크입니다.
  • 제안 해 주셔서 감사합니다. ‘ 그것. ‘ 코드 검토에 대해 몰랐습니다.
  • 답변

    @Gilles “답변 을 확장하려면 :

    사실 set -e가 작동하지 않습니다. 하위 셸에서 실행하더라도 || 연산자를 사용하는 경우 내부 명령; 예 : 작동하지 않습니다.

     #!/bin/sh # prints: # # --> outer # --> inner # ./so_1.sh: line 16: some_failed_command: command not found # <-- inner # <-- outer set -e outer() { echo "--> outer" (inner) || { exit_code=$? echo "--> cleanup" return $exit_code } echo "<-- outer" } inner() { set -e echo "--> inner" some_failed_command echo "<-- inner" } outer  

    하지만 || 연산자는 정리하기 전에 외부 함수에서 복귀하는 것을 방지하는 데 필요합니다.

    이 문제를 해결하는 데 사용할 수있는 약간의 트릭이 있습니다. 내부 명령을 백그라운드에서 실행 한 다음 즉시 좀 기다려.wait 내장 명령은 내부 명령의 종료 코드를 반환하며 이제는 iv id = “8a710934b9″다음에 “||를 사용합니다. >

    , 내부 함수가 아니므로set -e는 후자 내에서 제대로 작동합니다.

     #!/bin/sh # prints: # # --> outer # --> inner # ./so_2.sh: line 27: some_failed_command: command not found # --> cleanup set -e outer() { echo "--> outer" inner & wait $! || { exit_code=$? echo "--> cleanup" return $exit_code } echo "<-- outer" } inner() { set -e echo "--> inner" some_failed_command echo "<-- inner" } outer  

    다음은이 아이디어를 기반으로하는 일반 함수입니다. local 키워드, 즉 모든 local x=yx=y :

    로 바꿉니다. # [CLEANUP=cleanup_cmd] run cmd [args...] # # `cmd` and `args...` A command to run and its arguments. # # `cleanup_cmd` A command that is called after cmd has exited, # and gets passed the same arguments as cmd. Additionally, the # following environment variables are available to that command: # # - `RUN_CMD` contains the `cmd` that was passed to `run`; # - `RUN_EXIT_CODE` contains the exit code of the command. # # If `cleanup_cmd` is set, `run` will return the exit code of that # command. Otherwise, it will return the exit code of `cmd`. # run() { local cmd="$1"; shift local exit_code=0 local e_was_set=1; if ! is_shell_attribute_set e; then set -e e_was_set=0 fi "$cmd" "$@" & wait $! || { exit_code=$? } if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then set +e fi if [ -n "$CLEANUP" ]; then RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@" return $? fi return $exit_code } is_shell_attribute_set() { # attribute, like "x" case "$-" in *"$1"*) return 0 ;; *) return 1 ;; esac }  

    사용 예 :

     #!/bin/sh set -e # Source the file with the definition of `run` (previous code snippet). # Alternatively, you may paste that code directly here and comment the next line. . ./utils.sh main() { echo "--> main: $@" CLEANUP=cleanup run inner "$@" echo "<-- main" } inner() { echo "--> inner: $@" sleep 0.5; if [ "$1" = "fail" ]; then oh_my_god_look_at_this fi echo "<-- inner" } cleanup() { echo "--> cleanup: $@" echo " RUN_CMD = "$RUN_CMD"" echo " RUN_EXIT_CODE = $RUN_EXIT_CODE" sleep 0.3 echo "<-- cleanup" return $RUN_EXIT_CODE } main "$@"  

    예제 실행 :

     $ ./so_3 fail; echo "exit code: $?" --> main: fail --> inner: fail ./so_3: line 15: oh_my_god_look_at_this: command not found --> cleanup: fail RUN_CMD = "inner" RUN_EXIT_CODE = 127 <-- cleanup exit code: 127 $ ./so_3 pass; echo "exit code: $?" --> main: pass --> inner: pass <-- inner --> cleanup: pass RUN_CMD = "inner" RUN_EXIT_CODE = 0 <-- cleanup <-- main exit code: 0  

    이 방법을 사용할 때주의해야 할 것은 com에서 수행 된 모든 Shell 변수 수정 사항입니다. 명령이 서브 쉘에서 실행되기 때문에 run에 전달하면 명령이 호출 함수로 전파되지 않습니다.

    답변

    catch가 정확히 무슨 뜻인지 말하지 않습니다. —보고하고 계속하세요. 추가 처리를 중단 하시겠습니까?

    cd는 실패시 0이 아닌 상태를 반환하므로 다음을 수행 할 수 있습니다.

    cd -- "$1" && echo OK || echo NOT_OK 

    실패시 간단히 종료 할 수 있습니다.

    cd -- "$1" || exit 1 

    또는 자신의 메시지를 에코하고 종료합니다.

    cd -- "$1" || { echo NOT_OK; exit 1; } 

    및 / 또는 실패시 cd에서 제공 한 오류를 표시하지 않습니다.

    cd -- "$1" 2>/dev/null || exit 1 

    표준에 따라 명령은 STDERR (파일 설명자 2)에 오류 메시지를 넣어야합니다. 따라서 2>/dev/null는 STDERR을 /dev/null로 알려진 “bit-bucket”으로 리디렉션합니다.

    (잊지 마세요. 변수를 인용하고 cd에 대한 옵션의 끝을 표시합니다.

    댓글

    • @Stephane Chazelas의 인용 및 옵션 종료 시그널링 요령은 잘 잡혔습니다. 수정 해 주셔서 감사합니다.

    답변

    사실 귀하의 경우에는 논리가 개선 될 수 있다고 말씀 드리고 싶습니다.

    cd 대신 CD가 있는지 확인하고 존재하는지 확인한 다음 디렉토리로 이동하십시오.

    if [ -d "$1" ] then printf "${green}${NC}\\n" "$1" cd -- "$1" else printf "${red}${NC}\\n" "$1" fi 

    하지만 가능한 오류를 차단하는 것이 목적이라면 cd -- "$1" 2>/dev/null이지만 이는 향후 디버깅을 더 어렵게 만듭니다. if 테스트를 확인할 수 있습니다. 플래그 : 문서의 경우 배시 :

    댓글

    • 이 답변은 $1 변수를 인용하고 해당 변수가 공백 또는 기타 쉘 메타 문자를 포함합니다. 또한 사용자에게 cd 권한이 있는지 여부도 확인하지 못합니다.
    • 실제로 특정 디렉터리가 있는지 확인하려고했습니다. . 하지만 ‘ 더 잘 알지 못했기 때문에 CD로 이동하려고하면 존재하지 않으면 오류가 발생할 것이라고 생각했습니다. 그러니 잡을 수 없습니까? 나는 ‘ ‘가 내가 필요한 것이 정확히 무엇인지 [-d $ 1]에 대해 알지 못했습니다. 그래서 감사합니다! (저는 ‘ Java를 프로그래밍하는 데 사용되며 if 문에서 디렉토리를 확인하는 것은 Java에서 흔하지 않습니다.)

    답글 남기기

    이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다