ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Bash] 코딩 규칙 및 모범 사례
    Programming/Shell 2020. 1. 17. 16:55

     

    Naming and Styles

    Tabs and Spaces
    Tab 사용금지
    후행 공백 제거


    Pipe
    inline 파이프와 display 파이프가 있는데, 파이프가 너무 짧은 경우를 제외하고 display 파이프를 사용하여 명확하게 구분하자
    display 파이프를 사용할 파이프( | ) 기호 명령문 시작 부분에 넣자. ( 끝에 넣지말자)

    This is an inline pipe: "$(ls -la /foo/ | grep /bar/)"
    
    # The following pipe is of display form: every command is on
    # its own line.
    
    _foobar="$( \
      ls -la /foo/ \
      | grep /bar/ \
      | awk '{print $NF}')"
    
    _generate_long_lists \
    | while IFS= read -r  _line; do
        _do_something_fun
      done

    Variable names
    변수의 범위 따라 변수의 이름을 지정
    글로벌 변수는 대문자 ( EX. THIS_IS_A_USERXXX )
    외에 변수는 소문자, 밑줄(_) 시직한다. 밑줄(_) 주요 목적은 $변수가 사용될때 거리를 만들어서 코드를 쉽게 읽을 있다. (EX. $_this_is_a_xx)
    함수 정의 내부의 모든 로컬 변수는 local 명령문으로 선언하자
    local 명령문에 여러 변수를 선언할 있지만 그렇게하면 코드를 읽을 없기 때문에 라인별로 선언하자

    # The following variable can be provided by user at run time.
    D_ROOT="${D_ROOT:-}"
    
    # All variables inside `_my_def` are declared with `local` statement.
    _my_def() {
      local _d_tmp="/tmp/"
      local _f_a=
      local _f_b=
    
    # This is good, but it's quite a mess
      local _f_x= _f_y=
    }


    Function names
    내부 함수의 이름은 밑줄(_) 시작
    밑줄(_) 사용하여 동사와 명사를 붙이고, 낙타대문자를 사용하지 말자 (EX. ThisIsNotMyStle; use this_is_my_style )
    밑줄(__) 사용하여 다른 내부 함수에서 사용하는 경우에 사용하자

     

    Error handling

    Sending instructions
    모든 오류는 STDERR으로 보냄
    STDOUT 오류/경고 메시지를 보내지 말자
    echo 메시지를 직접 인쇄하는데 사용하지 말고, 대신 래퍼 사용 (warn, err, die) 사용하자

    _warn() {
      echo >&2 ":: $*"
    }
    
    _die() {
      echo >&2 ":: $*"
      exit 1
    }

    다른 기능의 오류를 처리하지 말고, 함수는 자체 정의 내에서 자체 구현으로 오류 메시지를 처리하자

    _my_def() {
      _foobar_call
    
    if [[ $? -ge 1 ]]; then
        echo >&2 "_foobar_call has some error"
        _error "_foobar_call has some error"
        return 1
      fi
    }

    위에 예에서 _my_def 대한 오류를 처리하는데, _foorbar_call 좋은 생각이 아니다

    _foobar_call() {
      # do something
    
    if [[ $? -ge 1 ]]; then
        _error "$FUNCNAME has some internal error"
      fi
    }
    
    _my_def() {
      _foobar_call || return 1
    }


    Catch up with $?
    $? 마지막의 리턴 코드를 얻는데 사용되는데, 가장 좋은 방법은 변수를 로컬 변수 저장하는 것이다

    _do_something_critical
    local _ret="$?"
    # from now, $? is zero, because the latest statement (assignment)
    # (always) returns zero.
    
    _do_something_terrible
    echo "done"
    if [[ $? -ge 1 ]]; then
      # Bash will never reach here. Because "echo" has returned zero.
    fi

    $? 매우 유용하지만, 너무 믿지 말자
    set -e   $? 함께 사용하지 말자
     

    Pipe error handling
    파이프는 구성 요소의 리턴 코드를 PIPESTATUS 배열에 저장한다
    변수는 파이프 뒤의 하위 파이프에서만 ONCE 사용할 있다 {shell, process }

    echo test | fail_command | something_else
    local _ret_pipe=( "${PIPESTATUS[@]}" )

    _ret_pipe 배열의 0 이외에 것이 포함 경우 일부 파이프 구성 요소가 실패했지 확인하자

    # Note:
    #   This function only works when it is invoked
    #   immediately after a pipe statement.
    _is_good_pipe() {
      echo "${PIPESTATUS[@]}" | grep -qE "^[0 ]+$"
    }
    
    _do_something | _do_something_else | _do_anything
    _is_good_pipe \
    || {
      echo >&2 ":: Unable to do something"
    }

     

    Automatic error handling

    set -u (매개변수 확장 설정되어 있지 않은 변수를 오류로 간주)
    항상 set -u 사용하여 선언되지 않은 변수를 사용하지 않도록 하자
    set -u 변수를 선언하고 값으로 설정하면 어쩔 없다.


    s
    et -e (0 아닌 상태값이면 증시 종료)
    스크립트를 전체 배포 할때는 주의하자 (가능하면 사용자가 선택할 있는 옵션)
    set -e 옵션을 이용하여 프로그램의 에러를 검출하고 싶을 때는 리턴코드를 생각하고 사용하자
    되도록 trap 이용하여 에러 검출하여 처리하는 로직이 좋다

    set -e
    _do_some_critical_check
    
    if [[ $? -ge 1 ]]; then
      echo "Oh, you will never see this line"
    fi

    _do_some_critical_check 실패, 스크립트는 종료하고 다음 코드는 예고없이 건너 뛴다

    (false && true); echo not here
    
    { false && true; }; echo here
    
    

     

    Techniques

    Make your script a library
    첫번째 function 사용하는데, 스크립트에 직접 지시문을 작성하는 대신 래퍼 존재한다. 아래 방법은 좋지 않다

    : do something cool
    : do something great

    function 포함시키는 것이 좋다

    _default_tasks() {
      : do something cool
      : do something great
    }

    마지막 줄에서 스크립트를 실행할 있다

    case "${@:-}" in
    ":")  echo "File included." ;;
    "")   _default_tasks        ;;
    esac

    다른 스크립트에서 코드를 실행하지 않고도 스크립트를 쉽게 포함시킬 수 있다
    간단한 기술을 발전시킴으로써 스크립트를 디버그하거나 스크립트 동작을 변경할 있는 많은 옵션을 갖는다

    # from other script
    source "/path/to_the_previous_script.sh" ":"


    Quick self-doc
    grep 다음 예제와 같이 사용하여 아름다운 자체 문서를 작성할 있다. 

    _func_1() { #public: Some quick introduction
      :
    }
    
    _func_2() { #public: Some other tasks
      :
    }
    
    _quick_help() {
      LANG=en_US.UTF_8
      grep -E '^_.+ #public' "$0" \
      | sed -e 's|() { #public: |☠|g' \
      | column -s"☠" -t \
      | sort
    }

    _quick_help 실행하면 다음과 같다

    _func_1    Some quick introduction
    _func_2    Some other tasks



    No excuse
    인수 목록이 비어 있으면 아무 것도하지 않고 즉시 종료해라
    기본 제약 건이 설정되지 않은 경우 종료해라


    Meta programming

    Bash 다음과 같이 매우 강력한 특징을 가지고 있다.

    my_func() {
      echo "This is my function`"
    }
    
    echo "The definition of my_func"
    declare -f my_func
    
    # <snip>

    중요할까?  (예를 들어 기능을 원격으로 전송하고 ssh 통해 해당 기능을 제외시킴)
    ssh 통해 많은 지시를 보내야 , 스크립트를 읽을 있도록 도와준다 (그러나 ssh 세션에 대화형 입력 스트림 누락됨)

    {
      declare -f my_func    # send function definition
      echo "my_func"        # execution instruction
    } \
    | ssh some_server


    Removing with care
    파일과 디렉토리를 올바르게 제거하기 힘들다
    백업 옵션과 함께 rm 사용하는 것을 고려해야한다
    rm 인수에 가지 변수를 사용하면 불변으로 만들 있다

    export _temporary_file=/path/to/some/file/
    readonly _temporary_file
    # <snip>
    rm -fv "$_temporary_file"


    Shell or Python/Ruby/etc
    한가지 중요한 요인은 bash 메모리 효율이 좋지않다. 여러 개의 데이터를 가지고 있다면, 데이터에 일정 부분을 추출하고 싶을 때마다 데이터를 다시 로드할 있다
    스크립트가 어떤 종류의 데이터라도 해석해야 , 앞으로 나아가서 다른 언어로 스크립트를 다시 쓰는 것이 좋다

     

    Good lessons

    See also in LESSONS.md (https://github.com/icy/bash-coding-style/blob/master/LESSONS.md).

    Resources

     

    Authors. License

    원저자는 Anh K. Huynh이며 원작은 TheSLinux의 일부였습니다.

    일부 기고자들이 오류를 수정하고 스타일을 개선하는 데 도움을 받았습니다. 그들은 또한 저자입니다.

    이 작업은 MIT 라이센스에 따라 릴리스됩니다.

     

    출처: <https://github.com/icy/bash-coding-style>

    'Programming > Shell' 카테고리의 다른 글

    [Bash] 스크립트 제어  (0) 2017.12.11
    [Bash] 쉘 스크립트 개요  (0) 2017.08.16

    댓글

작은거인's Blog / Designed by TISTORY