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 在运行前真正的脚本前,需要有参数错误的机制,如参数空白时,脚本应该出错。