shell grammer - msforest/notebook GitHub Wiki

shell 语法

  • shell 命令:即 ls/cd 等 linux 命令
  • shell 解释器:即 sh/bash/csh 等 shell 应用程序
  • shell 语法:即数据类型/变量/控制流语句/函数等编程语法

数据类型

shell 有四种数据类型

  • 数值型
  • 字符串型
  • 数组型
  • 列表型

不存在 boolean 类型,true/false 等同于字符串 true/false

数字操作

shell 和 js 一样,都是弱类型语言,变量的类型随时都是可变的,只有在引用的那一刻解释器才能知道存储的类型。

declare -i var_name=2 显示声明数字变量
var_name=2 隐式声明数值变量

算术运算符

  • 加:+、+=、++
  • 减:-、-=、--
  • 乘:*、*=
  • 除:/ (不支持'/=')
  • 取余 :%、%=

一元前缀++和前缀--,表示先做加(减)法,再输出结果
一元后缀++和后缀--,表示先输出结果,再做加(减)法

if 运算符

  • 等于:-eq
  • 不等于:-ne
  • 小于等于:-le
  • 大于等于:-ge
  • 大于:-gt
  • 小于:-lt
  • 逻辑与:&&
  • 逻辑非:!
  • 逻辑或:||

循环运算符

  • 等于:==
  • 不等于:!=
  • 小于等于:<=
  • 大于等于:>=
  • 大于:>
  • 小于:<
  • 逻辑与:&&
  • 逻辑非:!
  • 逻辑或:||

字符串操作

字符串存储不需要显示声明,默认存储的都是字符串类型,var_name=hello === var_name="hello"

  • length 返回字符串长度:${#Var_Name} (长度包括空白字符)
  • push 字符串相加
    • str+=string
    • str=${str}string
  • indexOf 字符串消除
    • ${var#*word}:查找var中自左而右第一个被word匹配到的串,并将此串及向左的所有内容都删除;此处为非贪婪匹配
    • ${var##*word}:查找var中自左而右最后一个被word匹配到的串,并将此串及向左的所有内容都删除;此处为贪婪匹配
    • ${var%word*}:查找var中自右而左第一个被word匹配到的串,并将此串及向右的所有内容都删除;此处为非贪婪匹配
    • ${var%%word*}:查找var中自右而左最后一个被word匹配到的串,并将此串及向右的所有内容都删除;此处为贪婪匹配
  • slice 字符串提取
    • ${var:offset}:自左向右偏移offset个字符,取余下的字串;例如:name=jerry,${name:2}结果为 rry
    • ${var:offset:length}:自左向右偏移offset个字符,取余下的 length 个字符长度的字串。例如:name=’hello world’ ${name:2:5}结果为llo w
  • replace 字符串替换
    • ${var/Pattern/Replaceplacement}:以Pattern为模式匹配var中的字串,将第一次匹配到的替换为Replaceplacement;此处为非贪婪匹配,Pattern模式可参考正则表达式
    • ${var//Pattern/Replaceplacement}:以Pattern为模式匹配 var 中的字串,将全部匹配到的替换为Replaceplacement;此处为贪婪匹配,Pattern模式可参考正则表达式

if 运算符

  • >:大于
  • <:小于
  • ==:等于,等值比较
  • =~:左侧是字符串,右侧是一个模式,判断左侧的字符串能否被右侧的模式所匹配:但是必须在中执行模式匹配。模式中可以使用行首、行尾锚定符,但是模式不要加引号,有时候可能不需要转义
  • !=,<>:不等于
  • -n:判断字符串是否不空,不空为真,空则为假
  • -z:判断字符串是否为空,空则为真,不空则假

数组操作

数组声明: declare -a arraydeclare -a array=()

  • length 返回数组长度(即有效元素的个数,不包括空元素)

    • ${#array[*]}
    • ${#array[@]}

    去掉#号就是显示数组项

  • indexOf 数组元素消除,该操作不会修改原数组元素,操作执行结果用数组来接收

    • array1=${array[*]#*word}:功能同下
    • array1=${array[*]##*word}:自左而右查找 array 数组中所有被匹配到的 word 匹配到的元素,并将所有匹配到的元素删除(并不会删除原数组中的元素),最后返回剩余的数组元素
    • array1=${array[*]%word*}:功能同下
    • array1=${array[*]%%word*}:自右而左查找 array 数组中所有被匹配到的 word 匹配到的元素,并将所有匹配到的元素删除(并不会删除原数组中的元素),最后返回剩余的数组元素
  • slice 数组元素提取,该操作不会修改原数组元素,操作执行结果用数组来接收

    • array1=${array[*]:offset}:返回 array 数组中索引为 offset 的数组元素以及后面所有元素;其中 offset 为整型数
    • array1=${array[*]:offset:length}:返回 array 数组中索引为 offset 的数值元素以及后面 length-1 个元素;其中 offset 和 length 都为整型数
  • replace 数组元素替换,该操作不会修改原数组元素,操作执行结果用数组来接收

    • array1=${array[*]/Pattern/Replaceplacement}:功能同下
    • array1=${array[*]//Pattern/Replaceplacement}:以 Pattern 为模式匹配 array 数组中的元素,将全部匹配到的替换为 Replaceplacement(不会修改原数组中的元素),并返回全部数组元素;Pattern 模式可参考正则表达式
    • array[n]=arg: 替换索引为 n 的元素
  • push 数组元素添加

    • array+=(item ...): 可添加一个也可添加多个

列表类型操作

函数

函数定义

function fn {
  # cmd
}

fn(){
  #cmd
}

function fn() {
  #cmd
}
  • ()与{}之间的空格可有可无
  • ()不能写入参,参数引用有特殊的定义 $1,$2,...$n
  • 函数要先声明,再调用,否则会报错

函数调用

fn arg1 arg2 ...
  • 位置参数

    • $0:和脚本位置参数一样,引用脚本名称
    • $1:引用函数的第 1 个传入参数
    • $n:引用函数的第 n 个传入参数
  • 特殊变量

    • $?:引用上一条命令的执行状态返回值,状态用数字表示 0-255
      • 0:表示成功
      • 1-255:表示失败;其中 1/2/127/255 是系统预留的,写脚本时要避开与这些值重复
    • $$:引用当前 shell 的 PID。除了执行 bash 命令和 shell 脚本时,$$不会继承父 shell 的值,其他类型的子 shell 都继承
    • $!:引用最近一次执行的后台进程 PID,即运行于后台的最后一个作业的 PID
    • $#:引用函数所有位置参数的个数
    • $*:引用函数所有位置参数的整体,即所有参数被当做一个字符串
    • $@:引用函数所有单个位置参数,即每个参数都是一个独立的字符串

上一条命令错误不会阻止后面的命令执行,即每条命令都是独立的

  • 参数移动(shift)
    在使用 shift 命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3 的值会移到$2 中,变量$2的值会移到$1 中,而变量$1的值则会被删除(注意,变量$0 的值,也 就是程序名,不会改变)。

函数选项处理函数getopt、getopts

函数退出

  • return [n]:可以在函数体内的任何地方使用,表示退出整个函数;数值 n 表示函数的退出状态码
    • 如果returnfunction之外,使用[.|source] filename此命令执行,则直接停止该执行操作,并返回给定状态码 n(如果未给定,则为 0)
    • 如果returnfunction之外,使用./filename此命令执行,将是一个错误用法
  • exit [n]:可以在脚本的任何地方使用,表示退出整个脚本(即退出当前进程);数值 n 表示脚本的退出状态码

函数作用域

  • 全局变量
  • 局部变量:在函数体内使用local关键字声明变量时为局部变量,否则都是全局变量

流程控制语句

查看命令信息: help if/case/case...

if

if TEST_COMMANDS; then
    COMMANDS_LIST;
[elif TEST_COMMANDS; then
    COMMANDS_LIST;]
# ...
[else
    COMMANDS_LIST;]
fi
  • TEST_COMMANDS 测试命令后面的分号(;)必不可少

case

case WORD in
    PATTERN1)
        COMMANDS_LIST
        ;;
    PATTERN2)
        COMMANDS_LIST
        ;&
    PATTERN3)
        COMMANDS_LIST
        ;;&
    # ...
esac
  • ;; 符号表示小分支执行完成后立即退出 case 语句
  • ;& 符号表示继续执行下一个小分支中的 COMMANDS_LIST 部分,而无需进行匹配动作,并由此小分支的结尾符号来决定是否继续操作下一个小分句
  • ;;& 符号表示继续向后(不止是下一个,而是一直向后)匹配小分支,如果匹配成功,则执行对应小分支中的 COMMANDS_LIST 部分,并由此小分支的结尾符号来决定是否继续向后匹配

select

select NAME [in WORDS ... ;] do
    COMMANDS_LIST
done

for

# 结构一
for NAME [in WORDS ... ] ; do
        COMMANDS_LIST
done

# 结构二
for (( exp1; exp2; exp3 )); do
        COMMANDS_LIST
done

while

while TEST_COMMANDS_LIST; do
        COMMANDS_LIST
done

util

until TEST_COMMANDS_LIST; do
        COMMANDS_LIST
done

退出命令

  • continue [n]:表示退出当前循环进入下一次循环,适用于 for、while、until、select 语句;n 表示退出的循环的次数,默认 n=1
  • break [n]:表示退出整个循环,适用于 for、while、until、select 语句;n 表示退出的循环层数,默认 n=1
  • return [n]:表示退出整个函数,适用于函数体内的 for、while、until、select 语句,同样也适用于函数体内的 if、case 语句;数值 n 表示函数的退出状态码,如果没有定义退出状态码,则函数的状态退出码为函数的最后一条命令的执行状态返回值
  • exit [n]:表示退出当前 shell,适用于脚本的任何地方,表示退出整个脚本;数值 n 表示脚本的退出状态码,如果没有定义退出状态码,则脚本的状态退出码为脚本的最后一条命令的执行状态返回值