어떻게 안전하고 기본적인 기능이 완비된 Bash 스크립트를 씁니까

11188 단어 bash스크립트
모든 사람이 많든 적든 가장 기초적인 Bash 스크립트를 작성하고 완성해야 하는 상황에 부딪힐 수 있다.사실은'와우, 나는 이 각본을 즐겨 쓴다'고 말할 사람이 없다.그래서 이 스크립트에 집중하는 사람은 드물다.
나 자신도 Bash 스크립트 전문가가 아니지만 본고에서 당신에게 가장 기초적이고 간단한 안전 스크립트 템플릿을 보여 드리겠습니다. 당신이 쓴 Bash 스크립트를 더욱 안전하고 실용적으로 만들 것입니다. 당신이 파악한 후에 틀림없이 많은 이익을 얻을 것입니다.

왜 Bash 스크립트를 써요?


사실 Bash 스크립트에 대한 가장 좋은 설명은 다음과 같다.
The opposite of "it's like riding a bike"is "it's like programming in bash".
A phrase which means that no matter how many times you do something, you will have to re-learn it every single time.
― Jake Wharton (@JakeWharton)
December 2, 2020
자전거를 타는 것과 반대로 몇 번을 해도 매번 다시 배우는 것 같다는 뜻이다.
그러나 Bash 스크립트 언어는 다른 널리 환영받는 언어, 예를 들어 자바스크립트와 마찬가지로 그들은 쉽게 갑자기 사라지지 않을 것이다. 비록 Bash 스크립트 언어는 업계의 주류 언어가 될 수 없지만 실제로 그는 우리 주위에 있고 없는 곳이 없다.
Bash는 셸의 사발을 물려받은 것처럼 모든 linux에서 그의 모습을 볼 수 있습니다. 이것은 대부분의 백엔드 프로그램이 실행되는 환경입니다. 따라서 서버의 응용 프로그램 시작, CI/CD 절차, 통합 테스트용 스크립트를 작성해야 할 때 Bash가 기다리고 있습니다.
몇 개의 명령을 붙여서 출력을 하나에서 다른 것으로 전달하고 실행 가능한 파일만 시작합니다. Bash는 여러 방안 중 가장 간단한 것입니다.다른 언어로 더 크고 복잡한 스크립트를 작성하는 것이 더 효과적이지만,Python,Ruby,fish 또는 당신이 생각하는 가장 좋은 프로그램을 어디에서든 컴파일해서 사용할 수 있다고 기대할 수는 없습니다.그래서 이를 어떤prod 서버, Docker image, CI 환경에 추가하기 전에 심사숙고하게 된다.
당연하지, Bash는 아직 완벽하지 않아.그의 문법은 초보자에게 악몽과 같다.잘못된 처리도 어렵다.도처에 우리가 처리해야 할 함정들이 있다.

Bash script template(Bash 스크립트 템플릿)


쓸데없는 말은 많이 하지 말고, 나의 틀을 바치시오

#!/usr/bin/env bash

set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)

usage() {
 cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]

Script description here.

Available options:

-h, --help   Print this help and exit
-v, --verbose  Print script debug info
-f, --flag   Some flag description
-p, --param   Some param description
EOF
 exit
}

cleanup() {
 trap - SIGINT SIGTERM ERR EXIT
 # script cleanup here
}

setup_colors() {
 if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
  NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
 else
  NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
 fi
}

msg() {
 echo >&2 -e "${1-}"
}

die() {
 local msg=$1
 local code=${2-1} # default exit status 1
 msg "$msg"
 exit "$code"
}

parse_params() {
 # default values of variables set from params
 flag=0
 param=''

 while :; do
  case "${1-}" in
  -h | --help) usage ;;
  -v | --verbose) set -x ;;
  --no-color) NO_COLOR=1 ;;
  -f | --flag) flag=1 ;; # example flag
  -p | --param) # example named parameter
   param="${2-}"
   shift
   ;;
  -?*) die "Unknown option: $1" ;;
  *) break ;;
  esac
  shift
 done

 args=("$@")

 # check required params and arguments
 [[ -z "${param-}" ]] && die "Missing required parameter: param"
 [[ ${#args[@]} -eq 0 ]] && die "Missing script arguments"

 return 0
}

parse_params "$@"
setup_colors

# script logic here

msg "${RED}Read parameters:${NOFORMAT}"
msg "- flag: ${flag}"
msg "- param: ${param}"
msg "- arguments: ${args[*]-}"

Choose Bash


#!/usr/bin/env bash
스크립트는 최상의 호환성을 얻기 위해/usr/bin/env를 인용합니다./bin/bash를 직접 인용하지 않습니다.

Fail fast


set -Eeuo pipefail
set 명령은 스크립트 실행 옵션을 변경할 수 있습니다.예를 들어, 일반적으로 Bash는 명령이 실패했는지 여부에 상관하지 않고 0이 아닌 종료 상태 코드를 되돌려줍니다.그것은 단지 빠르게 다음 단계로 뛰어올랐을 뿐이다.지금 이 작은 스크립트를 생각해 보세요.

#!/usr/bin/env bash
cp important_file ./backups/
rm important_file
백업 디렉터리가 존재하지 않으면 어떤 상황이 발생합니까?정확히 말하면, 컨트롤러에서 오류 메시지를 받을 수 있지만, 당신이 반응할 수 있기 전에, 이 파일은 두 번째 명령으로 삭제되었다.

Get the location


script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
이 코드는 스크립트의 위치 디렉터리를 정의할 수 있는 대로 cd 설정을 합니다.왜?
일반적으로 우리의 스크립트는 스크립트 위치에 대한 경로에서 실행되고 파일을 복사하며 명령을 실행합니다. 스크립트 디렉터리도 작업 디렉터리라고 가정합니다.네, 우리가 그것의 디렉터리에서 스크립트를 실행하기만 하면 됩니다.
하지만 CI 구성 실행 스크립트는 다음과 같습니다.

/opt/ci/project/script.sh
그러면 우리의 스크립트는 프로젝트 디렉터리에서 조작된 것이 아니라 CI 도구의 완전히 다른 작업 디렉터리에서 조작된 것이다.스크립트를 실행하기 전에 디렉토리로 이동하여 수정할 수 있습니다.

cd /opt/ci/project && ./script.sh
하지만 스크립트 차원에서 이 문제를 해결하는 것이 훨씬 낫다.따라서 스크립트가 같은 디렉터리에서 파일을 읽거나 다른 프로그램을 실행하면 다음과 같이 호출합니다.
cat "$script_dir/my_file"
또한 스크립트는 작업 디렉터리의 위치를 변경하지 않습니다.만약 스크립트가 다른 디렉터리에서 실행되고, 사용자가 어떤 파일을 가리키는 상대적인 경로를 제공한다면, 우리는 그것을 읽을 수 있습니다.

Try to clean up


trap cleanup SIGINT SIGTERM ERR EXIT

cleanup() {
 trap - SIGINT SIGTERM ERR EXIT
 # script cleanup here
}
스크립트가 끝날 때cleanup () 함수를 실행합니다.스크립트가 만든 모든 임시 파일을 삭제할 수 있습니다.
cleanup () 는 마지막에 호출할 수 있을 뿐만 아니라 언제든지 사용할 수 있다는 것을 기억하십시오.

Display helpful help


usage() {
 cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]

Script description here.

...
EOF
 exit
}
가능한 한 usage () 함수를 스크립트의 맨 위에 상대적으로 가깝게 하는 데는 두 가지 작용이 있다.
  • 모든 옵션을 모르고 발 전체를 보고 싶지 않은 사람에게 도움을 표시해야 한다.
  • 스크립트를 수정할 때 가장 작은 문서를 저장합니다. (2주 후에 어떻게 썼는지 기억조차 나지 않기 때문입니다.)
  • 나는 여기에 모든 함수를 기록하는 것을 주장하지 않는다.그러나 간단명료하고 아름다운 스크립트에서 이 메시지를 사용하는 것은 필수적이다.

    Print nice messages

    
    setup_colors() {
     if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
      NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
     else
      NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
     fi
    }
    
    msg() {
     echo >&2 -e "${1-}"
    }
    우선, 텍스트에서 색을 사용하고 싶지 않으면, setup_colors () 함수입니다.내가 그것을 보류하는 것은 내가 매번 구글로 인코딩할 필요가 없다면 색을 더욱 자주 사용할 수 있다는 것을 알고 있기 때문이다.
    그 다음에 이 색들은 echo 명령이 아니라 msg () 함수에만 사용됩니다.
    msg () 함수는 스크립트가 출력하지 않은 모든 내용을 인쇄하는 데 사용됩니다.이것은 오류가 아니라 모든 로그와 메시지를 포함한다.인용하다12 Factor CLI Apps에 대한 설명:
    In short: stdout is for output, stderr is for messaging.
    ― Jeff Dickey, who knows a little about building CLI apps
    stdout는 출력에 사용되고, stderr는 메시지 전달에 사용됩니다.
    이것이 바로 대부분의 경우 stdout를 위해 색을 사용해서는 안 되는 이유입니다.
    msg () 로 인쇄된 메시지는 stderr 흐름에 전송되고 색채와 같은 특수한 시퀀스를 지원합니다.stderr 출력이 상호작용 단말기가 아니거나 표준 매개 변수를 전달하면 색이 비활성화됩니다.
    사용법은 다음과 같습니다.
    
    msg "This is a ${RED}very important${NOFORMAT} message, but not a script output value!"
    stderr가 상호작용 단말기인지 확인하려면 스크립트에 위와 같은 줄을 추가하십시오.그리고 stderr를 stdout로 리디렉션하고 파이프를 통해cat로 보냅니다.파이핑 작업은 출력을 터미널에 직접 보내지 않고 다음 명령으로 보내므로 색상이 비활성화됩니다.
    
    $ ./test.sh 2>&1 | cat
    This is a very important message, but not a script output value!

    Parse any parameters

    
    parse_params() {
     # default values of variables set from params
     flag=0
     param=''
    
     while :; do
      case "${1-}" in
      -h | --help) usage ;;
      -v | --verbose) set -x ;;
      --no-color) NO_COLOR=1 ;;
      -f | --flag) flag=1 ;; # example flag
      -p | --param) # example named parameter
       param="${2-}"
       shift
       ;;
      -?*) die "Unknown option: $1" ;;
      *) break ;;
      esac
      shift
     done
    
     args=("$@")
    
     # check required params and arguments
     [[ -z "${param-}" ]] && die "Missing required parameter: param"
     [[ ${#args[@]} -eq 0 ]] && die "Missing script arguments"
    
     return 0
    }
    만약 스크립트에서 매개 변수화가 의미가 있다면, 나는 보통 전체 스크립트를 한 곳에서만 사용하더라도 할 것이다.그것은 복제와 재사용을 더욱 쉽게 하는데, 이것은 보통 아침저녁으로 발생한다.그리고 어떤 것들은 하드 인코딩이 필요하더라도 Bash 스크립트보다 높은 단계에서 더 좋은 위치를 차지한다.
    CLI 매개변수에는 플래그, 명명 매개변수 및 위치 매개변수의 세 가지 주요 유형이 있습니다.parse_params () 함수는 이 모든 매개 변수를 지원합니다.
    여기에 처리되지 않은 유일한 공공 매개 변수 모드는 여러 개의 알파벳 로고를 연결하는 것이다.두 개의 로고를 -ab로 전달하기 위해서는 -a-b가 아니라 추가 코드가 필요합니다.
    while 순환은 수동으로 파라미터를 해석하는 방법이다.다른 언어에서는 내장된 해상도나 사용할 수 있는 라이브러리를 사용해야 하지만, 알겠습니다. 이것은 Bash입니다.
    템플릿에 예시 로고 (-f) 와 명명 매개 변수 (-p) 가 있습니다.다른 매개 변수를 추가하기 위해 변경하거나 복사하기만 하면 됩니다.이후에 usage () 를 업데이트하는 것을 잊지 마십시오.
    여기서 가장 중요한 것은 첫 번째 구글 결과를 사용하여 Bash 파라미터를 해석할 때 알 수 없는 옵션의 오류를 잃어버리는 것입니다.스크립트가 알 수 없는 옵션을 받았다는 사실은 사용자가 스크립트가 수행할 수 없는 작업을 원한다는 것을 의미합니다.그래서 사용자의 기대와 스크립트 행위는 크게 다를 수 있다.가장 좋은 것은 나쁜 일이 발생하기 전에 처형을 완전히 막는 것이다.
    Bash에서 매개변수를 해석하는 데는 두 가지 선택이 있습니다.하나하나어떤 사람들은 그것들의 사용을 찬성하고 반대한다.기본적으로 macOS의 getopt 동작이 완전히 다르기 때문에 getopts는 긴 인자 (예를 들어 --help) 를 지원하지 않습니다.

    Using the template


    인터넷에서 찾은 대부분의 코드처럼 복사해서 붙여라.
    복제 후 4가지만 변경:
  • 스크립트 설명을 포함하는 usage () 텍스트
  • cleanup() 내용
  • parse_params()의 매개 변수 C는 --help와 --no color를 유지하지만 예시를 바꾸기: -f와 -p
  • 실제 스크립트 논리
  • Portability


    나는 맥 OS에서 이 템플릿 (기본 bash3.2 사용) 과 몇 개의 Docker 이미지를 테스트했다. 데비안, Ubuntu, CentOS,amazonlinux, Fedora.그것은 확실히 작용했다.
    분명히, 그것은 Bash가 부족한 환경에서 일할 수 없다. 예를 들어alpinellinux.

    Further reading


    Bash 또는 다른 언어로 CLI 스크립트를 만들 때 공통 규칙이 있습니다.이러한 리소스는 다음과 같이 소형 스크립트와 대형 CLI 애플리케이션을 신뢰할 수 있는 방법을 안내합니다.
    Command Line Interface Guidelines( https://clig.dev/ )
    12 Factor CLI Apps( https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46 )
    Command line arguments anatomy explained with examples( https://betterdev.blog/command-line-arguments-anatomy-explained/ )

    Closing notes


    나는 Bash 스크립트 템플릿을 만드는 첫 번째도 아니고 마지막도 아니다.이 프로젝트는 나의 일상적인 수요에 대해 너무 크지만 좋은 선택이다.어쨌든, 나는 가능한 한 Bash 스크립트를 작게 하려고 한다.
    Bash 스크립트를 작성할 때 JetBrains IDE와 같은 ShellCheck linter를 지원하는 IDE를 사용하십시오.그것은 네가 그 반대의 일을 하는 것을 막을 것이다.
    이상은 안전하고 기본적인 기능이 완선한 Bash 스크립트의 상세한 내용을 어떻게 작성하는지, 안전하고 기본적인 기능이 완선한 Bash 스크립트를 작성하는지에 대한 자료는 저희 다른 관련 글에 주목하세요!

    좋은 웹페이지 즐겨찾기