Shell語法規範

来源:https://www.cnblogs.com/Rohn/archive/2020/06/14/13123790.html
-Advertisement-
Play Games

ver:1.0 博客:https://www.cnblogs.com/Rohn 本文介紹了Shell編程的一些語法規範,主要參考依據為谷歌的Shell語法風格。 背景 博客:https://www.cnblogs.com/Rohn 使用哪一種Shell 可執行文件必須以 #!/bin/bash 和最 ...


目錄

背景

博客:https://www.cnblogs.com/Rohn

使用哪一種Shell

可執行文件必須以 #!/bin/bash 和最小數量的標誌開始。請使用 set 來設置shell的選項,使得用 <script_name>調用你的腳本時不會破壞其功能。

推薦使用:

#!/usr/bin/env bash

env一般固定在/usr/bin目錄下,而其餘解釋器的安裝位置就相對不那麼固定。

限制所有的可執行Shell腳本為bash使得我們安裝在所有電腦中的shell語言保持一致性。

無論你是為什麼而編碼,對此唯一例外的是當你被迫時可以不這麼做的。其中一個例子是Solaris SVR4包,編寫任何腳本都需要用純Bourne shell

[root@test ~]# echo $SHELL
/bin/bash

什麼時候使用Shell

使用Shell需要遵守的一些準則:

  • 如果你主要是在調用其他的工具並且做一些相對很小數據量的操作,那麼使用Shell來完成任務是一種可接受的選擇。
  • 如果你在乎性能,那麼請選擇其他工具,而不是使用Shell。
  • 如果你發現你需要使用數據而不是變數賦值(如 ${PHPESTATUS} ),那麼你應該使用Python腳本。
  • 如果你將要編寫的腳本會超過100行,那麼你可能應該使用Python來編寫,而不是Shell。

請記住,當腳本行數增加,儘早使用另外一種語言重寫你的腳本,以避免之後花更多的時間來重寫。

註釋

博客:https://www.cnblogs.com/Rohn

Bash只支持單行註釋,使用#開頭的都被當作註釋語句。

頂層註釋

每個文件必須包含一個頂層註釋,對其內容進行簡要概述。版權聲明和作者信息是可選的。
例如:

#!/usr/bin/env bash
# Author: Rohn
# Version: 1.0
# Created Time: 2020/06/06
# Perform hot backups of MySQL databases.
  • 第1行,指明解釋器,使用bash

#!叫做"Shebang"或者"Sha-bang"(Unix術語中,#號通常稱為sharp,hash或mesh;而!則常常稱為bang),指明瞭執行這個腳本文件的解釋程式。當然,如果使用bash test.sh這樣的命令來執行腳本,那麼#!這一行將會被忽略掉。

  • 第2-5行,分別為作者、版本號、創建時間、功能說明。

功能註釋

任何不是既明顯又短的函數都必須被註釋。任何庫函數無論其長短和複雜性都必須被註釋。

其他人通過閱讀註釋(和幫助信息,如果有的話)就能夠學會如何使用你的程式或庫函數,而不需要閱讀代碼。

所有的函數註釋應該包含:

  • 函數的描述
  • 全局變數的使用和修改
  • 使用的參數說明
  • 返回值,而不是上一條命令運行後預設的退出狀態

例如:

#!/usr/bin/env bash
# Author: Rohn
# Version: 1.0
# Created Time: 2020/06/06
# Perform hot backups of Oracle databases.

export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin'

#######################################
# Cleanup files from the backup dir
# Globals:
#   BACKUP_DIR
#   ORACLE_SID
# Arguments:
#   None
# Returns:
#   None
#######################################
cleanup() {
  ...
}

TODO註釋

TODOs應該包含全部大寫的字元串TODO,接著是括弧中你的用戶名。冒號是可選的。最好在TODO條目之後加上bug或者ticket的序號。

例如:

# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)

格式

博客:https://www.cnblogs.com/Rohn

縮進

縮進兩個空格,沒有製表符。例如:

if [ a > 1 ];then
  echo '${a} > 1'
fi

行的長度和長字元串

行的最大長度為80個字元。例如:

# DO use 'here document's
cat <<END;
I am an exceptionally long
string.
END

# Embedded newlines are ok too
long_string="I am an exceptionally
  long string."

管道

如果一行容不下整個管道操作,那麼請將整個管道操作分割成每行一個管段。

應該將整個管道操作分割成每行一個管段,管道操作的下一部分應該將管道符放在新行並且縮進2個空格。這適用於使用管道符|的合併命令鏈以及使用||&&的邏輯運算鏈。

例如:

# All fits on one line
command1 | command2

# Long commands
command1 \
  | command2 \
  | command3 \
  | command4

迴圈

if-else語句

if; then放在同一行,;後空一格,else單獨一行,fi單獨一行,並與if垂直對齊。即:

if condition; then
  statement(s)
else
  statement(s)
fi

for-do和while-do語句

while/for; do放在同一行,donewhile/for垂直對齊,即:

# while structure
while condition; do
  statement(s)
done

# for structure
for condition; do
  statement(s)
done

例如:

for dir in ${dirs_to_cleanup}; do
  if [[ -d "${dir}/${ORACLE_SID}" ]]; then
    log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
    rm "${dir}/${ORACLE_SID}/"*
    if [[ "$?" -ne 0 ]]; then
      error_message
    fi
  else
    mkdir -p "${dir}/${ORACLE_SID}"
    if [[ "$?" -ne 0 ]]; then
      error_message
    fi
  fi
done

case語句

  • 通過2個空格縮進可選項。
  • 在同一行可選項的模式右圓括弧之後和結束符 ;;之前各需要一個空格。
  • 長可選項或者多命令可選項應該被拆分成多行,模式、操作和結束符;;在不同的行。

匹配表達式比caseesac 縮進一級。多行操作要再縮進一級。一般情況下,不需要引用匹配表達式。模式表達式前面不應該出現左括弧。避免使用;&;;&符號。即:

# case structure
case in expression in
  pattern1)
    statement1
    ;;
  pattern2)
    statement2
    ;;
  ...
  *)
    statementn
    ;;
esac

例如:

case "${expression}" in
  a)
    variable="..."
    some_command "${variable}" "${other_expr}" ...
    ;;
  absolute)
    actions="relative"
    another_command "${actions}" "${other_expr}" ...
    ;;
  *)
    error "Unexpected expression '${expression}'"
    ;;
esac

只要整個表達式可讀,簡單的命令可以跟模式和;; 寫在同一行。這通常適用於單字母選項的處理。當單行容不下操作時,請將模式單獨放一行,然後是操作,最後結束符;; 也單獨一行。當操作在同一行時,模式的右括弧之後和結束符;;之前請使用一個空格分隔。

verbose='false'
aflag=''
bflag=''
files=''
while getopts 'abf:v' flag; do
  case "${flag}" in
    a) aflag='true' ;;
    b) bflag='true' ;;
    f) files="${OPTARG}" ;;
    v) verbose='true' ;;
    *) error "Unexpected option ${flag}" ;;
  esac
done

變數擴展

按優先順序順序:保持跟你所發現的一致;引用你的變數;推薦用${var}而不是$var

例如

# Section of recommended cases.

# Preferred style for 'special' variables:
echo "Positional: $1" "$5" "$3"
echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ ..."

# Braces necessary:
echo "many parameters: ${10}"

# Braces avoiding confusion:
# Output is "a0b0c0"
set -- a b c
echo "${1}0${2}0${3}0"

# Preferred style for other variables:
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
while read f; do
  echo "file=${f}"
done < <(ls -l /tmp)

# Section of discouraged cases

# Unquoted vars, unbraced vars, brace-quoted single letter
# shell specials.
echo a=$avar "b=$bvar" "PID=${$}" "${1}"

# Confusing use: this is expanded as "${1}0${2}0${3}0",
# not "${10}${20}${30}
set -- a b c
echo "$10$20$30"

特性

博客:https://www.cnblogs.com/Rohn

命令替換

使用 $(command)而不是反引號。

嵌套的反引號要求用反斜杠轉義內部的反引號。而$(command) 形式嵌套時不需要改變,而且更易於閱讀。

例如:

# This is preferred:
var="$(command "$(command1)")"

# This is not:
var="`command \`command1\``"

文件名的通配符擴展

當進行文件名的通配符擴展時,請使用明確的路徑。

因為文件名可能以-開頭,所以使用擴展通配符./**來得安全得多。

# Here's the contents of the directory:
# -f  -r  somedir  somefile

# This deletes almost everything in the directory by force
psa@bilby$ rm -v *
removed directory: `somedir'
removed `somefile'

# As opposed to:
psa@bilby$ rm -v ./*
removed `./-f'
removed `./-r'
rm: cannot remove `./somedir': Is a directory
removed `./somefile'

命名約定

博客:https://www.cnblogs.com/Rohn

函數名

使用小寫字母,並用下劃線分隔單詞。使用雙冒號 :: 分隔庫。函數名之後必須有圓括弧。關鍵詞 function 是可選的,但必須在一個項目中保持一致。

如果你正在寫單個函數,請用小寫字母來命名,並用下劃線分隔單詞。如果你正在寫一個包,使用雙冒號 :: 來分隔包名。大括弧必須和函數名位於同一行(就像在Google的其他語言一樣),並且函數名和圓括弧之間沒有空格。

# Single function
my_func() {
  ...
}

# Part of a package
mypackage::my_func() {
  ...
}

當函數名後存在 () 時,關鍵詞 function 是多餘的。但是其促進了函數的快速辨識。

變數名

使用小寫字母,迴圈的變數名應該和迴圈的任何變數同樣命名。例如:

for zone in ${zones}; do
  something_with "${zone}"
done

常量和環境變數名

全部使用大寫字母,用下劃線分隔,聲明在文件的頂部。例如:

# Constant
readonly PATH_TO_FILES='/some/path'

# Both constant and environment
declare -xr ORACLE_SID='PROD'

源文件名

使用小寫字母,如果需要的話使用下劃線分隔單詞。例如: maketemplate 或者 make_template ,而不是 make-template

只讀變數

使用小寫字母,使用 readonly 或者 declare -r 來確保變數只讀。

因為全局變數在Shell中廣泛使用,所以在使用它們的過程中捕獲錯誤是很重要的。當你聲明瞭一個變數,希望其只讀,那麼請明確指出。

zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
if [[ -z "${zip_version}" ]]; then
  error_message
else
  readonly zip_version
fi

使用本地變數

使用小寫字母,使用 local 聲明特定功能的變數。聲明和賦值應該在不同行。

使用 local 來聲明局部變數以確保其只在函數內部和子函數中可見。這避免了污染全局命名空間和不經意間設置可能具有函數之外重要性的變數。

當賦值的值由命令替換提供時,聲明和賦值必須分開。因為內建的 local 不會從命令替換中傳遞退出碼。

my_func2() {
  local name="$1"

  # Separate lines for declaration and assignment:
  local my_var
  my_var="$(my_func)" || return

  # DO NOT do this: $? contains the exit code of 'local', not my_func
  local my_var="$(my_func)"
  [[ $? -eq 0 ]] || return

  ...
}

調用命令

博客:https://www.cnblogs.com/Rohn

檢查返回值

對於非管道命令,使用$?或直接通過一個if語句來檢查以保持其簡潔。例如:

if ! mv "${file_list}" "${dest_dir}/" ; then
  echo "Unable to move ${file_list} to ${dest_dir}" >&2
  exit "${E_BAD_MOVE}"
fi

# Or
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; then
  echo "Unable to move ${file_list} to ${dest_dir}" >&2
  exit "${E_BAD_MOVE}"
fi

Bash也有 PIPESTATUS 變數,允許檢查從管道所有部分返回的代碼。如果僅僅需要檢查整個管道是成功還是失敗,以下的方法是可以接受的:

tar -cf - ./* | ( cd "${dir}" && tar -xf - )
if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then
  echo "Unable to tar files to ${dir}" >&2
fi

可是,只要你運行任何其他命令, PIPESTATUS 將會被覆蓋。如果你需要基於管道中發生的錯誤執行不同的操作,那麼你需要在運行命令後立即將 PIPESTATUS 賦值給另一個變數(別忘了 [ 是一個會將 PIPESTATUS 擦除的命令)。

tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
return_codes=(${PIPESTATUS[*]})
if [[ "${return_codes[0]}" -ne 0 ]]; then
  do_something
fi
if [[ "${return_codes[1]}" -ne 0 ]]; then
  do_something_else
fi

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...
  • 痞子衡剛剛拿到i.MXRT1170 B0版本的晶元,迫不及待地在上面跑了一些A0版本上早已驗證過的demo,功能一切正常,沒有什麼額外遷移工作。因為目前只有B0版本晶元,沒有配套EVK,所以痞子衡是在RT1170內部Validation板上做測試的(RT主晶元以及Flash晶元全部放在Socket里... ...
  • [root@minimal html]# locate mysql.log locate: 無法執行 stat () `/var/lib/mlocate/mlocate.db': 沒有那個文件或目錄 解決方案: updatedb 再次測試: [root@minimal html]# locate n ...
  • 上一篇文章講了 Nginx 中的變數和運行原理,下麵就來說一個主要提供變數並修改變數的值的模塊,也就是我們要講的防盜鏈模塊:referer 模塊。 簡單有效的防盜鏈手段 場景 如果做過個人站點的同學,可能會遇到別人盜用自己站點資源鏈接的情況,這就是盜鏈。說到盜鏈就要說一個 HTTP 協議的 頭部,r ...
  • 背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3. ...
  • Linux 下,如果要執行一條或多條之前輸過的指令,要怎麼處理?很多人會想到使用上下箭頭去翻查歷史輸入的命令。這當然是可以了,除了這種方法,本文再介紹另外 5 種方法來實現這樣的效果。 在正式開始之前,我們先隨便執行幾條命令,便於後面演示。為了節約篇幅,執行結果暫時不列出來。 $ ls -l $ p ...
  • 學習Linux的註意事項(一) Linux嚴格區分大小寫 Linux是嚴格區分大小寫的,這一點和Windows不一樣,所以操作時要註意區分大小寫的不同,包括文件名和目錄名、命令、命令選項、配置文件配置選項等 Linux一切皆文件 Linux中所有內容都是以文件的形式保存和管理的,硬體設備也是文件,這 ...
  • Step 1: 安裝vncserver和其依賴的軟體 # 修改yum為國內鏡像 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup-linux && \ wget -O /etc/yum.repo ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...