首頁>技術>

幾乎任何人遲早都要寫一個Bash指令碼。幾乎沒有人說“是的,我喜歡寫”。這就是為什麼幾乎每個人在編寫它們時都會注意的不多的原因。

Bash繼承了Shell寶座,幾乎可以在所有Linux(包括Docker映像)上找到。這就是大多數後端執行的環境。因此,如果需要指令碼化伺服器應用程式啟動,CI / CD步驟或整合測試執行的指令碼,那麼Bash可以滿足需求。

為了將幾個命令粘合在一起,將輸出從一個傳遞到另一個,然後僅啟動一些可執行檔案,Bash是最簡單,最原生的解決方案。儘管用其他語言編寫更大,更復雜的指令碼是很有意義的,但不能指望Python,Ruby,fish或你認為最好的其他直譯器隨處可見。在將其新增到產品伺服器,Docker映像或CI環境之前,可能應該三思而後再考慮。

但是Bash遠非完美。語法是一場噩夢。錯誤處理很困難。到處都有地雷。我們必須處理它。

Bash指令碼模板

事不宜遲,就在這裡。

#!/usr/bin/env bashset -Eeuo pipefailtrap cleanup SIGINT SIGTERM ERR EXITscript_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)usage() {  cat <<EOFUsage: $(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 descriptionEOF  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 heremsg "${RED}Read parameters:${NOFORMAT}"msg "- flag: ${flag}"msg "- param: ${param}"msg "- arguments: ${args[*]-}"

現在讓我們更詳細地研究它。

環境相容
set -Eeuo pipefail

為了獲得最佳相容性,它引用/usr/bin/env而不是/bin/bash直接引用。

快速失敗
#!/usr/bin/env bashcp important_file ./backups/rm important_file

該set命令更改指令碼執行選項。例如,通常Bash不在乎某些命令是否失敗,返回一個非零的退出狀態程式碼。它只是希望地跳到下一個。現在看這個小指令碼:

#!/usr/bin/env bashcp important_file ./backups/rm important_file

如果backups目錄不存在,將會發生什麼?你會在控制檯中收到一條錯誤訊息,但是在你能夠做出反應之前,第二條命令已經刪除了該檔案。

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

此行盡其所能定義指令碼的location目錄,然後為什麼定義它。

假設指令碼目錄也是一個工作目錄,指令碼通常在相對於指令碼位置的路徑上執行,複製檔案並執行命令。而且,只要從指令碼目錄執行指令碼即可。

但是,假設CI配置執行如下指令碼:

/opt/ci/project/script.sh

那麼指令碼不是在專案目錄中執行,而是在CI工具的某些完全不同的工作目錄中執行。可以透過執行指令碼之前轉到目錄來修復它:

cd /opt/ci/project && ./script.sh

但是在指令碼方面解決這個問題要好得多。因此,如果指令碼從同一目錄中讀取某些檔案或執行另一個程式,請按以下方式呼叫它:

cat "$script_dir/my_file"

同時,指令碼不會更改工作目錄位置。如果指令碼是從其他目錄執行的,並且使用者提供了某個檔案的相對路徑,則仍然可以讀取該檔案。

嘗試清理
trap cleanup SIGINT SIGTERM ERR EXITcleanup() {  trap - SIGINT SIGTERM ERR EXIT  # script cleanup here}

考慮一下該指令碼trap的finally塊。在指令碼結尾處–由錯誤或外部訊號引起的正常–該cleanup()功能將被執行。例如,在這裡您可以嘗試刪除指令碼建立的所有臨時檔案。

只要記住,cleanup()不僅可以在結束時呼叫,還可以讓指令碼完成工作的任何部分。嘗試清除的所有資源不一定都將存在。

顯示有用的幫助
usage() {  cat <<EOFUsage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]Script description here....EOF  exit}

具有usage()相對接近指令碼的頂部,它會在兩個方面採取行動:

向不瞭解所有選項並且不想遍歷整個指令碼來發現它們的幫助,作為修改指令碼時的最小文件(例如,你兩週後甚至根本不記得寫過指令碼)。列印好訊息
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()如果不想在文字中使用顏色,請刪除該功能。之所以保留它是因為,不必每次都用Google搜尋顏色程式碼,可以會更頻繁地使用顏色。

其次,這些顏色msg()只能與功能一起使用,而不能與echo命令一起使用。

msg()函式用於列印不是指令碼輸出的所有內容。這不僅包括錯誤,還包括所有日誌和訊息。

簡而言之:stdout用於輸出,stderr用於訊息傳遞。

這就是為什麼在大多數情況下stdout都不應該使用顏色的原因。

msg()傳送有訊息的訊息被髮送到stderr流中並支援特殊序列。

用法:

msg "This is a ${RED}very important${NOFORMAT} message, but not a script output value!"
解析任何引數
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}

CLI引數主要有三種類型-標誌,命名引數和位置引數。該parse_params()功能支援所有這些。

唯一的公共引數模式(此處未處理)是連線的多個單字母標誌。為了能夠將兩個標誌傳遞為-ab,而不是-a -b,將需要一些其他程式碼。

該while迴圈是解析引數的手動方式。在所有其他語言中,您應該使用內建解析器或可用的庫之一,但是,這就是Bash。

模板中包含示例標誌(-f)和命名引數(-p)。只需更改或複製它們即可新增其他引數。並且不要忘了更新usage()。

Bash中有兩種解析引數的方法。是getopt和getopts。有贊成和反對使用它們的論點。這些工具不是最好的,因為預設情況下getoptmacOS上的行為完全不同,並且getopts不支援長引數(例如--help)。

使用模板

只需將其複製貼上即可,就像網際網路上找到的大多數程式碼一樣。

對於Bash,沒有通用npm install等效項。

複製之後,只需要更改4件事:

usage() 帶有指令碼描述的文字cleanup() 內容引數parse_params()-保留--help和--no-color,但替換示例:-f和-p實際的指令碼邏輯可移植性

在MacOS(預設,古老的Bash 3.2)和幾個Docker映像上測試了該模板:Debian,Ubuntu,CentOS,Amazon Linux,Fedora有用。

11
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Celluloid讓matplotlib動畫-2:紅綠燈