Bash 手冊

數值比較及運算

比較字串、數值及檔案

字串比較

str1 = str2

str1 等於 str2

str1 != str2

str1 不等於 str2

str1 < str2

str1 小於 str2

str1 > str2

str1 大於 str2

-n str

str 長度大於零

-z str

str 長度為零

數值比較

n1 -eq n2

n1 等於 n2

n1 -ge n2

n1 大於或等於 n2

n1 -gt n2

n1 大於 n2

n1 -le n2

n1 小於或等於 n2

n1 -lt n2

n1 小於 n2

n1 -ne n2

n1 不等於 n2

檔案比較

-d file

file 為目錄

-e file

file 是否存在

-f file

file 為檔案

-r file

file 為可讀

-s file

file 不為空

-w file

file 可寫

-x file

file 為執行檔

-O file

file 為用戶擁有

-G file

file 檔案群組為用戶群

F1 -nt F2

F1 比 F2 還新

F1 -ot F2

F1 比 F2 還舊

字串運算

大小寫轉換

STR="Hello"
echo $STR
# Hello

# 轉大寫
echo ${STR^^}
# HELLO

# 轉小寫
echo ${STR,,}
# hello

取出分隔字串及檔案拆解

STR='t1@t2@t3'

# 去掉前面的分隔字元 @
echo "${STR#*@}"
# t2@t3

# 去掉前面所有分隔字元 @
echo "${STR##*@}"
# t3

# 去掉後面的分隔字元 @
echo ${STR%@*}
# t1@t2

# 去掉後面所有分隔字元 @
echo ${STR%%@*}
# t1
檔案路徑拆解 (為上述的延伸)
SRCFile=/var/www/File.txt

# 去掉路徑,去掉前面所有分隔字元 /
echo ${SRCFile##*/}
# File.txt

# 副檔名,去掉前面所有分隔字元 .
echo ${SRCFile##*.}
# txt

# 去掉副檔名,去掉後面的分隔字元 .
echo ${SRCFile%.*}
# /var/www/File

# 去掉路徑及副檔名
SRCFile=/var/www/File.txt
FileName=${SRCFile##*/}
echo $FileName
# File.txt
echo ${FileName%.*}
# File

# 目前路徑
echo $PWD

# 目前父路徑,去掉後面的分隔字元 /
echo ${PWD%/*}

取出部份字串

SRCFile=File.txt

# 取出字串第 1 字後長度 4, bash 為 Base 0 (指開始位置為 0) 開始位置要減 1
# 或者將第一個位置視為 0, 則不用減。
echo ${SRCFile:0:4}
# File

# 取出字串第 6 字後長度 3, 開始位置要減 1。
# 第一個位置視為 0, 第 5 字後長度 3。
echo ${SRCFile:5:3}
# txt

取代字串

STR='1 2 3'

# 取代一個空白字元為 ,
echo ${STR/ /,}
# 1,2 3

# 取代所有空白字元為 ,
echo ${STR// /,}
# 1,2,3

符合部分字串

String="JohnLisa"

Part="Jo"
if [[ $String == *$Part*  ]]; then \
  echo Part $Part mach String; \
fi;

Part="Li"
if [[ $String == *$Part*  ]]; then \
  echo Part $Part mach String; \
fi;

Part="x"
if [[ $String != *$Part*  ]]; then \
  echo Part $Part not mach String; \
fi;

檔案及路徑

取得父路徑
# 取得父路徑
dirname /var/www/drupal/README.txt
# /var/www/drupal
取得檔案名稱或最底層的目錄
basename /var/www/drupal/README.txt
# README.txt

basename /var/www/drupal
# drupal
取得檔案的父目錄名稱
readmeFile=/var/www/drupal/README.txt
parentPath=$(basename $(dirname $readmeFile))
echo $parentPath
# drupal

Bash Scripts

set -x -e -u

-x -e -u
#!/bin/bash -x
# +x +e +u 不用設定,原本就是預設,-x -e -u 才要設定。

# 顯示每個命令行
set -x

# 不顯示每個命令行
set +x

# 發生錯誤時立即退出
set -e

# 未初始化變數時,退出程序。
set -u
在 bash 中讀取具有預設值的變數
read -p "Do you want to continue? [Y/n] " yn
yn=${yn:-y} (1)
if [ "${yn}" != "Y" ] && [ "${yn}" != "y" ]; then
  exit 1
fi
1 若無輸入則 yn 預設為 y${parameter:-word} 參閱: Shell Parameter Expansion (Bash Reference Manual)

解析 (建構中)

todo

循環使用逗號分隔的shell變量 Loop through a comma-separated shell Itmes

流程

if elif else fi
cat >/tmp/demo-sh <<'EOF'
#!/bin/bash
if [ "$1" == "1" ] || [ "$1" == "2" ]; then  (1) (3)
  echo Param1 is 1 or 2
elif [ "$1" != "9" ] && ! [ "$2" != "9" ]; then (2)
  echo Param1 is not 9, and Parm2 is 9
else
  echo "I don't know the parameters"
fi
EOF
chmod +x /tmp/demo-sh
1 == 等於,|| 或 (or) 運算。
2 != 不等於,&& 且 (and) 運算,! 否 (not) 運算。
3 變數前後應加上雙引號 (") 或單引號 (')。
/tmp/demo-sh 1
/tmp/demo-sh 3 9
for done 範例
Itmes=1abc,2def,3ghi

unset IFS
for Item in ${Itmes//,/ }; \
do \
  echo "$Item"; \
done;

IFS=","
for Item in $Itmes; \
do \
  echo "$Item"; \
done
unset IFS


for indexhtml in $(find src/$lang \( -name "html-$sid.ad" -or -path "*$sid/html-*.ad" \)); do
done

for srcfile in src/$lang/$parentpath/$id/*.adoc; do
done

以 cat 輸出至指令檔

  • 管道終止字串有雙 (單) 引號,避免替換變數 ($)。

  • 在 Bash 中輸入 TAB 鍵,需要取消 tab completion (意思是 TAB 鍵會被清成空值) 功能, 以 bind "set disable-completion on" 取消後再輸入 「TAB 鍵」, 再以 bind "set disable-completion off" 回復成啟用。
    註: 在 Cygwin 中需安裝 bash-completion 套件。

disable-completion 測試
bind "set disable-completion off"
TAB_Key='	' (1)
echo "this is a '${TAB_Key}' tab key"
# this is a '' tab key

bind "set disable-completion on" (2)
echo "this is a '${TAB_Key}' tab key"
# this is a '' tab key

bind "set disable-completion on" (3)
TAB_Key='	'
echo "this is a '${TAB_Key}' tab key"
# this is a '     ' tab key
bind "set disable-completion off"
1 在 tab completion 的情況下,不能讀取「TAB 鍵」這是 readline 問題。
2 輸入「TAB 鍵」後之後,再設定 set disable-completion on 並無作用。
3 輸入「TAB 鍵」之前,先設定 set disable-completion on。

如何在 Bash Scripts 中解析命令行參數

Bash 空格分隔(例如 --option 參數)

demo-options.sh
bind "set disable-completion on"
cat >demo-options.sh <<'EOF'
#!/bin/bash

tab='	'
nl='
'
IFS=" $tab$nl"

# ${0##*/} 去掉前面所有分隔字元 / (路徑)只留下指令檔名
usage="Usage: ${0##*/} [OPTION] ...

demo-options:
  -u, --unsafe		unsafe argument
  -s, --safe		safe argument
  -d, --default		default no argument
  -h, --help		display this help and exit"

Arguments=()  # 設置陣列變數
while [[ $# -gt 0 ]]; do
  case $1 in
    -u|--unsafe)
      UNSAFE="$2"
      shift # 跳過引數
      shift 2;; # 跳過引數值
    -s|--safe)
      if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then (1)
        SAFE=$2
        shift # 跳過引數
      else
        echo "safe argument unknown" >&2
        exit 1
      fi
      shift;; # 跳過引數值
    -d|--default)
      DEFAULT=YES
      shift;; # 跳過已解析的引數
    -h|--help)
      echo "$usage"; exit 1;;
    -*|--*) #
      echo "unknown option: $1" >&2;
      exit 1;;
    *)  # 其他引數
      Arguments+=("$1") # 非選項引數儲存至陣列備用
      shift;; # 跳過已解析的引數
  esac
done

set -- "${Arguments[@]}" # 將陣列變數回存

echo "UNSAFE    = " $UNSAFE
echo "SAFE      = " $SAFE
echo "DEFAUL    = " $DEFAULT
echo "Unresolved= " $@
EOF
bind "set disable-completion off"

chmod +x demo-options.sh
./demo-options.sh --unsafe unsafe_ARG -d p1
1 引數值空白時,指令碼引發錯誤。
執行結果
UNSAFE    =  unsafe_ARG
SAFE      =
DEFAUL    =  YES
Unresolved=  p1
錯誤測試
./demo-options.sh --unsafe unsafe_ARG -d p1
./demo-options.sh --unsafe            -d p1
錯誤輸出
UNSAFE    =  -d (1)
SAFE      =
DEFAUL    =
Unresolved=  p1
1 出錯,bash 在執行前真正的指令碼前,需要有參數錯誤的機制,如參數空白時,指令碼應該出錯。