Shell编程基础

变量

定义、使用、删除

  • 定义变量

    • 定义时不加$符号
    • 变量名和等号之间不能有空格
    • 变量名只能由英文字母、数字、下划线,且不以数字开头, 不能用bash中的关键字
    • 已经定义变量可以重新定义
  • 使用变量

    • 即可使用, 未定义变量直接使用不报错,返回空值
    • {}可选,但是{}能够明确定义变量名范围,帮助阅读、 解释器执行
  • 匿名变量

    • 可以认为所以语句(命令)的执行结果都存储在一个匿名 变量中,可以在其之前加上$获取其结果
  • readonly:设置变量为只读变量,更改(重定义)变量报错

  • unset:删除变量,不能删除只读变量

局部变量

局部变量:在脚本、命令中定义

  • 仅在当前shell实例中有效

    • shell变量默认为global,作用域从被定义处开始到脚本 末尾(即使在函数内部定义)
    • 显式local声明作用域局限于函数内部
  • 其他shell启动的程序不能访问局部变量

环境变量

环境变量:就是shell中的普通变量,只是这些变量往往被进程读取 用于自身初始化、设置

  • 狭义:export/declare -x声明的变量,只有这样的变量 才能默认被子进程继承
  • 广义;shell中所有的变量(包括局部变量)

环境变量设置

在shell中设置环境变量有两种方式

  • export/declare -x:设置全局(环境)变量

    • 任何在该shell设置环境变量后,启动的(子)进程都会 继承该变量
    • 对于常用、许多进程需要的环境变量应该这样设置
  • <ENV_NAME>=... cmd:设置临时环境变量

    • <ENV_NAME>=...不能被子进程继承,所以必须在其后立刻 接命令
    • 只对当前语句有效,也不能覆盖同名变量

用途

  • 环境变量是某些程序正常运行的必要条件
  • 所有程序都能访问环境变量,可用作进程通信
    • 在程序中设置环境变量,供其他进程访问变量
    • 父进程为子进程设置环境变量,供其访问

Shell变量

Shell变量:shell程序设置的特殊变量,包括环境变量、局部变量, 保证shell的正常运行

  • $0:shell执行脚本名

    • 交互式bash(即普通终端中)该参数为-bash
      • source执行脚本时,该参数可能非预期
    • 以下shell变量均不考虑$0,如
      • 函数中$0也是脚本名
      • shift无法跳过该参数
  • $1,..., $9:向脚本传递的位置参数

  • $@:脚本、函数参数列表
  • $*:脚本、函数参数字符串
  • $#:脚本、函数参数数量
  • $?:上条命令执行结果
  • $$$$:脚本进程号
  • $!:执行脚本最后一条命令

字符串

拼接

  • shell中字符默认为字符串,自动拼接

通配符

  • *:匹配任意长度字符串

    • 不包括./,必须显式匹配
  • ?:匹配一个字符

  • []:匹配出现在[]中的字符

    1
    2
    ls /[eh][to][cm]*
    # 匹配`/etc`、`/home`等
  • {}

    • 枚举全部

      1
      2
      3
      4
      5
      6
      $ mkdir {user1, user2}-{home, bin}
      # 笛卡尔积(字符串连接)
      # 等价于建立`user1-home, user1-bin, user2-home, user2-bin`
      $ echo {1..10}
      # 生成序列
      # 见`for`中`RangeIterator`部分

子串

1
2
$ file=/dir1/dir2/dir3/file.txt.bak
$ null=""

切片

  • ${<var_name>:n[:m]}:从第n开始m个字符
    • nm非负值
      • n:从0开始的下标起始
      • m切片长度缺省表示到末尾
    • nm使用0-负值表示
      • n:从0开始的负向下标起始
      • m:从0开始的负向下标终止
命令 解释 结果
${file:0:5} 提取首个字符开始的5个字符 /dir1
${file:5:5} 提取第5个字符开始的5个字符 /dir2
${file:5} 提取第5个字符开始至末尾 /dir2...
${file:0-5} 反向计算下标 t.bak
${file:0-5:0-1} 反向计算下标 t.ba
${file:5:0-2} 提取第5个字符开始至-2下标处 /dir2.../t.b

子串匹配

1
2
3
4
${<var_name>%<pattern>}
${<var_name>%%<pattern>}
${<var_name>#<pattern>}
${<var_name>##<pattern>}
  • #:去掉字符串左边最短pattern
  • ##:去掉字符串左边最长pattern
  • %:去掉字符串右边最短pattern
  • %%:去掉字符串右边最长pattern
  • 注意:var_name前不要变量标志$
  • 以上4种模式返回新值,不改变原变量值
  • 仅在pattern中使用通配符时,最短、最长匹配才有区别
命令 解释 结果
${file#*/} 去除首个/及其左边 dir2/dir3/file.txt.bak
${file##*/} 仅保留最后/右边 file.txt.bak
${file#*.} 去除首个.及其左边 txt.bak
${file##*.} 仅保留最后.右边 bak
${file%/*} 去除最后/及其右边 /dir1/dir2/dir3
${file%%*/} 去除首个/及其右边 空值
${file%*.} 去除最后.及其右边 /dir1/dir2/dir3/file.txt
${file%%*.} 去除首个.及其右边 /dir1/dir2/dir3/file.txt

替换

  • /from/to:替换首个fromto
  • //from/to:替换全部fromto
命令 解释 结果
${file/dir/path} 替换首个dirpath /path1/dir2/dir3/file.txt.bak
${file/dir/path} 替换全部dirpath /path1/path2/path3/file.txt.bak

默认值替换

  • -:变量未设置返回
  • +:变量设置返回
  • =:变量未设置返回、设置变量
  • ?:变量未设置输出至stderr
  • ::以上命令条件包括空值(空值视为未设置)

shell_variable_assignment

命令 解释 示例 结果
${井<var_name>} 获取字符串长度 ${井file} 27
${<var_name>-<default>} 变量未设置返回默认值 ${invalid-file.txt.bak} file.txt.bak
${<var_name>:-<default>} 变量未设置、空值返回默认值 ${null-file.txt.bak} file.txt.bak
${<var_name>+<default>} 变量设置返回默认值 ${file-file.txt.bak} fil.txt.bak
${<var_name>:+<default>} 变量非空返回默认值 ${file-file.txt.bak} file.txt.bak
${<var_name>=<default>} 变量未设置,返回默认值、并设置变量为默认值 ${invalid=file.txt.bak} file.txt.bak
${<var_name>:=<default>} 变量未设置、空值返回默认值、并设置变量为默认值 ${null=file.txt.bak} file.txt.bak
{$<var_name>?<default>} 变量未设置输出默认值至stderr {invalid?file.txt.bak} file.txt.bak输出至stderr
{$<var_name>:?<default>} 变量未设置、空值输出默认值至stderr {$null:?file.txt.bak} file.txt.bak输出至stderr

字符串比较

  • =, !=, -z, -n:字符串相等、不相等、为空、非空

  • 使用=比较变量是否为某字符串时,其中在两侧添加字符 保证=不为空值,否则若$test为空值,表达式报错

    1
    2
    3
    if [[ "$text"x = "text"x ]]; then
    command
    fi
  • 比较变量时要在两侧加上双引号"",否则可能报错、结果不 符合预期

数组

1
$ A=(a b c def)

取值

  • []:选择数组元素
    • [n]:返回数组第n个元素
    • [*]/[@]:返回数组全部元素
命令 解释 结果
${A[@]} 返回全部元素 a b c def
${A[*]} 同上 同上
${A[0]} 返回数组第一个元素 a
${井A[@]} 返回数组元素总数 4
${井A[*]} 同上 同上
${井A[3]} 返回数组第4个元素长度 3
A[3]=xyz 设置数组第4个元素值

数值

  • (())/[]:在其中执行整数运算
  • let:执行数值运算

规则

  • 返回的数据结果相当于存储在一个匿名变量中,使用的话需要 在之前加上$

  • 1
    2
    3
    $ a=5; b=7; c=2;
    $ echo $((a + b * $c))
    # 19
  • $((N#xx))/$[$#xx]:将其他进制数据转换为10进制

    1
    2
    $ echo $((2#110))
    # 6
  • 自增、自减运算可以在(())/[]中直接执行

    1
    2
    $ a=5; ((a++)); echo $a;
    # 6

特殊运算符

  • ~:位与
  • |:位或
  • ^:位异或
  • >>:位右移
  • <<:位左移
  • **:数值平方
  • +=, ++, -=, --, *=, \=:自变化运算符

逻辑运算

  • -eq, -ne, -ge, -le, -lt, -gt:比较
  • ==, !=, >=, <=, <, >:数值比较

文件

  • -e:目标存在
  • -f:文件为一般文件(非目录、设备)
  • -s:文件大小非0
  • -d:目标为目录
  • -b:文件为块设备(软盘、光驱)
  • -c:文件为流设备(键盘、modem、声卡)
  • -p:文件为管道
  • -h/L:目标为符号链接
  • -S:目标为Socket
  • -t:文件描述符被关联到终端
    • 一般用于检测脚本的stdin是否来自终端[ -t 0 ],或 输出是否输出至终端[ -t 1 ]
  • -r:目标是否可读权限
  • -w:目标是否可写权限
  • -x:目标是否可执行权限
  • -g:目标是否设置有set-group-id
  • -u:目标是否设置有set-user-id
  • -k:目标是否设置sticky bit
  • -O:用户是否是文件所有者
  • -G:用户是否属于文件所有组
  • -N:文件从上次读取到现在为止是否被修改
  • f1 -nt f2:文件f1f2
  • f1 -ot f2:文件f1f2
  • f1 -ef f2:文件f1f2指向相似文件实体(相同的硬链接)

Shell执行控制

符号

命令执行

  • ;:连续执行指令,分割多条命令

    • 所以如果不是在一行内执行,;是可以有任意多个
  • ::内建空指令,返回值为0

  • &&, ||:逻辑与、或,全部(任意)之前的命令执行成功才会 执行下连接中的命令

“转义”

  • \\:转义字符

    • 放在指令前,取消alias
    • 特殊符号前转义,Shell将不对其后字符作特殊处理,当作 普通字符(即$, \', \"
    • 行尾表示连接下一行,回车符只起换行作用,视为空格
  • $:变量符号,表示其后字符串为一个变量

  • \', \":内部视为字符串

  • --:命令行中转义-,否则shell认为-是参数选项

    1
    2
    $ rm -- -file
    # 删除文件`-file`
    • 还可以在之前加上路径避免-被解释为参数选项
    • \''等均无效

引号

单引号''

  • 单引号括起字符都作为普通字符,变量无效

  • 单引号字串中不能出现单独的单引号,使用转义符也不行,但是 可以成对出现,作为字符串拼接使用

双引号""

  • 双引号字串中可以有变量、转义字符

反引号``

反引号扩起字符串表示Shell解释命令,执行时shell首先解释其, 以标准输出替代整个反引号

  • 括号中语句为命令,分号连接

    • ``中的表达式只需要符合C语言的运算规则 即可,甚至可以使用三目预算符、逻辑表达式
  • 命令和括号之间无空格

  • $``可以将命令输出作为变量返回值,同$()

括号

()

  • 命令组/运算表达式:其中的命令列表将在新子shell运行

    • 其中变量之后不能使用,不影响当前shell环境
    • 多个命令之间分号分隔,最后命令可以省略
    • 命令和括号之间可以无空格
  • 命令替换:等同于引号``

    • bash扫描命令行,执行$()结构中命令,将标准输出作为 匿名变量值
    • ()中出现()不需要转义,``需要转义
    • ()中不能使用C风格的运算表达式
    • 部分shell可能不支持(),如:tcsh
  • 初始化数组:参见数组

(())

  • 整数扩展:计算、扩展算数表达式结果

    • 表达式结果为0则退出状态为1、false
    • 表达式结果为1则退出状态为0、true
    1
    2
    a=5;((a++))			# 自增运算符
    b=$((16#5f)) # 进制转换
    • 退出状态和[]完全相反
    • 其中可使用任何符合C语言的运算,包括三目运算符
  • 算术比较:

    • 其中变量可以不使用$前缀、支持多个表达式逗号分隔
    • 可直接使用C风格表达式,如:forwhile循环语句中
1
2
3
4
5
6
7
for((i=0;i<5;i++))
for i in `seq 0 4`
for i iin {0..4}
# 三者等价
if ((i<5))
if [ $i -lt 5 ]
# 二者等价

[][[]]

  • []

    • 条件测试表达式:参见if
    • 数组索引
    • 正则表达式范围
  • [[]]

    • 条件测试表达式:参见if

{}

  • 创建匿名函数

    • 不开启新进程
    • 括号内变量之后可继续使用
    • 括号内命令分号分隔,最后一个语句也要有分号
    • {和第一个语句之间要有空格
  • 字符串

    • 默认值替换:参见字符串默认值替换
    • 获取子串:参见字符串子串
    • 获取切片:参见字符串子串
    • 字符串替换:参见字符串子串
  • $后包括变量名精确分隔变量名,参见变量使用

其他符号

!

Shell中!称为事件提示符,可以方便的引用历史命令:当! 后跟随字母不是空格、换行、回车、=(时,做命令替换

  • ![-]n:引用history中的正数/倒数第n个命令

    • !!:等同!-1,执行上条命令
  • !cmd:引用最近以cmd开头的命令,包括参数

    • !cmd:gs/pattern/replace:将上条cmd开头命令中 pattern替换为replace
  • !?str[?]:引用最近包含str的命令,str可以是参数

  • 参数引用

    • !$:最后一个参数
    • !:-:除最后一个参数
    • !*:所有参数

控制语句

if[]

用于条件控制结构,结构如下

1
2
3
4
if test_expression; then
then command_1
else command_2
fi
  • test <expr>:bash内部命令,判断表达式是否为真

    • 其后只能有一个表达式判断,无法使用逻辑与、或连接
  • [ <expr> ]:基本等同于test

    • 整数比较:-ne-eq-gt-lt
    • 字符串比较:转义形式大、小号\</>
    • 逻辑表达式连接:-o(或)、-a(与)、!(非) (不能使用||(或)、&&(与))
    1
    2
    3
    if [ $? == 0 ]
    if test $? == 0
    # 二者等价
    • 注意内部两边要有空格
    • 事实上是[等同于test]表示关闭条件判断,新版 bash强制要求闭合
  • [[ <expr> ]]条件测试

    • 整数比较:!==<>
    • 字符串:支持模式匹配(此时右侧模式时不能加引号)
    • 逻辑表示式连接:||(或)、&&(与)、!(非)
    1
    2
    3
    4
    if [[ $a != 0 && $b == 1 ]]
    if [ $a -eq 0 ] && [ $b -eq 1]
    if [ $a -eq 0 -a $b -eq 1]
    # 三者等价
    • [[:bash关键字,bash将其视为单独元素并返回退出 状态码

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case chars in
char_pattern_1)
# `)`是必要的
# 匹配即停止,不会下沉
command_1
;;
# 每个模式字符串可以有多条命令,最后一条必须以`;;`结尾
char_pattern_2 | char_pattern_3)
# `|`分隔,或,可以匹配其中一个模式即可
command_2
;;
*)
# 匹配所有字符串模式,捕获所有
command_3
;;
esac
# 返回值位最后执行命令的退出值,未执行命令则为0

while

1
2
3
4
5
6
while test_expression
# test_expression是指`test`、`[]`语句
# 测试条件为真进入循环体
do
command_1
done

until

1
2
3
4
5
until test_exprssion
# 测试条件为假进入循环体
do
command_1
done

for

CStyle

1
2
3
4
5
for ((i=0; i<limit; i++))
# C-style for
do
command_1
done

ListStyle

1
2
3
4
5
for var in var_arr
# list-style for
do
command
done
FileIterator
1
2
3
4
5
6
for var in /path/to/file_regex
# `var`依次取(当前)目录下的与正则表达式匹配的文件名
# 执行循环体语句
do
command
done
  • /path/to/file_regex注意
    • 路径不存在:$var为该字符串
    • 不能用""括起,否则:$var只取路径下所有匹配合并 ,空格分隔
ParamsIterator
1
2
3
4
5
6
for var[ in $*]
# `var`依次取位置参数的值,执行循环体中命令
# `[ in $*]`可以省略
do
command
done
RangeIterator
1
2
3
4
5
6
7
8
9
for i in $(seq start end)
do
command
done

for i in {start..end}
do
command
done

break, continue, exit

  • break[n]:跳出n层循环体,默认跳出一层循环体
  • continue[n]:跳过n层循环体在其之后的语句,回到循环开头 ,默认跳过一次循环体
  • exit:退出程序,后跟返回值

Bash 编程技巧

检查命令是否成功

  • 原版

    1
    2
    3
    4
    5
    6
    7
    echo abcdee | grep -q abcd

    if [ $? -eq 0 ]; then
    echo "found
    else
    echo "not found"
    fi
  • 简洁版

    1
    2
    3
    4
    5
    if echo abcdee | grep -q abc; then
    echo "found"
    else
    echo "not found"
    fi
  • 精简版

    1
    echo abcdee | grep -q abc && echo "found" || echo "not found"

标准输出、错误输出重定向到/dev/null

  • 原版

    1
    $ grep "abc" text.txt 1>/dev/null 2>&1
  • 简洁版

    1
    $ grep "abc" text.txt &> /dev/null

awk使用

  • 原版
    1
    $ sudo xm li | grep vm_name | awk `{print $2}`
  • 简洁版
    1
    $ sudo xm li | awk `/vm_name/{print $2}`

逗号连接所有行

  • 原版:sed

    1
    $ sed ":a;$!N;s/\n/,;ta" test.txt
  • 简洁:paste

    1
    $ paste -sd, /tmp/test.txt

过滤重复行

  • 原版:sort

    1
    $ sort text.txt | unique
  • 简洁版

    1
    $ sort -u text.txt

Bash 脚本执行

脚本执行

#!

脚本首行注释

  • 文件执行时,shell会将文件内容发送至#!之后的解释器上
  • 不限于shell脚本,也适合其他类型的脚本语言
    • 当然要求#为脚本语言支持的注释符
  • 建议在首行使用env命令而不是硬编码解释器路径,这样可以 减少对机器的依赖
    1
    2
    #!/usr/bin/env python
    #!/usr/bin/env shell

4种执行方式

  • $ file_name:表示在当前shell执行文件,需要有文件的执行 权限

  • $ source/. file_namesource.意义一致,表示读取 文件内容在shell中,然后在shell里执行文件内容,需要读权限

  • $ sh file_name:表示开启一个新的shell进程,并读取文件 内容在新的shell中然后执行,同样的需读权限

以下面的文件test.sh为例

1
2
3
4
5
#!/bin/bash
echo "fisrt"
sleep 1000
echo "second"
slepp 1000
  • $ test.sh:产生两个新进程test.sh和sleep,在second输出 之前<ctrl-c>,会同时终止两个进程,不会继续输出second

  • $ sh test.sh:产生两个新进程,shell和sleep,在second 输出之前<ctrl-c>,同样的会同时终止两个进程,不会继续 输出second(实际上first是在新的shell里输出的)

  • source/. test.sh:产生一个新进程sleep,在second输出之前 <ctrl-c>,只有sleep进程被终止,而test.sh的内容在当前 shell里直接执行,当前shell没有被终止,second会继续输出

结论

  • 如果需要对当前shell进行设置,应该使用source/.

  • 否则这三种执行方式除了进程产生、权限要求应该没有差别

Shell环境

环境变量

  • 设置环境变量直接:$ ENV_NAME=value=前后不能有空格) 但是此时环境变量只能在当前shell中使用,需要export命令 导出之后才可在子shell中使用

  • 环境变量设置代理

    1
    2
    export http_proxy=socks://ip
    export https_proxy=socks5://ip

输入输出

  • 0:STDIN_FILENO,标准输入
  • 1:STDOUT_FILENO,标准输出
  • 2:STDERR_FILENO,标准错误

说明:

  • 标准输出和标准错误虽然都是在命令行上显示,但是两个是 不同的流。输出重定向>只能将标准输出重定向,不会将 标准错误重定向,其仍然会在命令行显示

重定向

  • <:重定向标准输入
  • >:标准输出write重定向
  • >>:标准输出add重定向
  • 2>:标准错误重定向

说明:

  • 输出重定向就是1 > outputcommand > output只是省略 1的简写

    • 命名自己不是输出,不能重定向
    • 是命令产生的标准输出重定向
  • 可以通过2>&1将标准错误重定向到标准输出,从而将命令的 标准输出、错误输出同时输出

    • &这里表示等效于标准输出(标准输出的引用)
    • command>a 2>acommand>a 2>&1看起来类似,实际上 前者写法会打开两次a,stderr覆盖stdout,而后者是 引用,不会覆盖、IO效率更高
    • 2>&1放在后面大概是因为需要获取stdout的引用,所以 需要标准输出先给出重定向
    • 这种写法还有个简写&>>&
  • 可以重定向到设备,在*nix系统中,设备也被视为文件

    1
    $ echo "hello" > /dev/tty01

    特别的设备/dev/null可以作为不需要输出的重定向目标

HereDoc Format

HereDoc Format:特殊的重定向,指示shell从标准输入读取输入 直至遇到标记行(前后不能有空白符)

1
2
3
<CMD> << <EOF>
here-doc
<EOF>
  • EOF:标记字符串,常用EOFSTOP
  • 若标记中含有字符被quote(如被引号包括)

    • 结束标记为去除quotation结果
    • here-doc中不执行参数扩展、命令替换、算数扩展
  • 若标记中没有quotation

    • here-doc中执行参数扩展、命令替换、算数扩展
    • 此时\$、反引号需要\转义

管道

|:将一个程序的标准输出发送到另一个程序的标准输入

  • 一些平台会并行启动以管道连接的程序,此时使用类似迭代器 输出、输入能提高效率

Shell内置命令

  • which命令无法找到的命令

set

set:设置所使用的shell选项、列出shell变量

  • :不带参数,显示全部shell变量
  • -a:输出之后所有至export(环境变量)
  • -b:使被终止后台程序立即汇报执行状态
  • -B:执行括号扩展
  • -C:重定向所产生的文件无法覆盖已存在文件
  • -d:shell默认使用hash表记忆已使用过的命令以加速 执行,此设置取消该行为
  • -e:若指令回传值不为0,立即退出shell
  • -f:取消使用通配符
  • -h:寻找命令时记录其位置???
  • -H:(默认)允许使用!<编号>方式执行history中 记录的命令
  • -k:命令=被视为设置命令的环境变量
  • -m:监视器模式,启动任务控制
    • 后台进程已单独进程组运行
    • 每次完成任务时显示包含退出的状态行
  • -n:读取命令但不执行
    • 通常用于检查脚本句法错误
  • -p:允许set-user/group-id
    • 禁止处理$ENV文件、从文件中继承shell函数
  • -P:处理cd等改变当前目录的命令时,不解析符号链接
  • -t:读取、执行下条命令后退出
  • -u:使用未设置变量作为错误处理
  • -v:输入行被读取时,显示shell输出行
  • -x:显示简单命令的PS4扩展值(包括所有参数)、当前命令 的环境变量
  • -o:option-name,下列之一
    • allexport:同-a
    • braceexpand shell:(默认)执行花括号扩展
    • emacs:(默认)使用emacs风格命令行编辑接口
    • errexit:同-e
    • errtrace:同-E
    • functrace:同-T
    • hashall:同-h
    • histexpand:同-H
    • history:记录命令历史
    • ignoreeof:读取EOF时不退出shell
    • interactive-comments:允许交互式命令中出现注释
    • keyword:同-k
    • monitor:同-m
    • noclobber:同-C
    • noexec:同-n
    • noglob:同-f
    • noglob:currently accepted but ignored
    • nohash:同-d
    • notify:同-b
    • nounset:同-u
    • physical:同-P
    • pipfail:管道命令返回值为最后返回值非0命令的状态, 若没有非0返回值返回0
    • posix:改变shell属性以匹配标准,默认操作不同于 POSIX1003.2标准
    • priviledged:同-p
    • verbose:同-v
    • vi:使用vi风格的命令编辑接口
    • xtrace:同-x
  • +:加以上参数取消标志位设置(包括o参数)
  • --:给所有剩余参数设置标志位,没有剩余参数则unset
  • -:给所有剩余参数设置标志位
  • $-中存放有当前已设置标志位

let

let:执行计算的工具,用于执行一个、多个表达式

  • 变量计算中不需要$标识变量
  • 表达式中包含特殊字符,需要引起来

declare

declare:声明变量内容

  • -a:声明变量为普通数组
  • -A:声明变量为关联数组(下标支持字符串,类似字典)
  • -i:声明变量为整形
  • -r:声明变量只读
  • -x:设置为环境变量(export
  • -g:在函数中创建全局变量
  • +:和以上联合使用,取消定义的变量类型

  • -f:列出脚本中定义的函数名称、函数体

  • -F:列出脚本中定义的函数名称
  • -p:查看变量属性、值

read

read:从标准输入读入变量内容

1
2
$ read [<option>] <var>[ <var2>...]
$ read -p "Enter names: " name1, nem
  • 读取值数目大于变量数目时,多余值均赋给最后变量
  • -p:命令行提示内容
  • -n:不换行
  • -d:分隔标记
  • -t:阻塞等待时常,之后返回非零
  • -s:隐藏输入
  • -r:反折号不被视为转义标记
1
2
3
cat README | while read -r line; do
echo $line
done

shift

shift:移除参数列表中头部部分参数,缺省移除1个

  • shift移除从$1开始,无法移除$0

getopts

getopts:逐个读取解析单字符指示的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function func (){
echo OPTIND: $OPTIND
while getopts ":a:B:cdef" opt; do
case $opt in
a) echo "this is -a the arg is ! $OPTARG at $OPTIND" ;;
B) echo "this is -B the arg is ! $OPTARG at $OPTIND" ;;
c) echo "this is -c the arg is ! $OPTARG at $OPTIND" ;;
\?) echo "Invalid option: -$OPTARG" ;;
esac
done
echo OPTIND: $OPTIND
echo $@
shift $(($OPTIND - 1))
echo $@

}
func -a 23 -B 1904-03-04 343 age
  • $OPTARG:参数值
  • $OPTIND:在参数列表中位移,初始值为1,常配合shift 使用剔除已处理参数
  • 遇到未指定option则返回0
  • option字符后::字符可以带有参数,赋给$OPTARG;否则 仅表示标志,仅该字符被处理

  • option_string开头:可以避免错误输出

    • :标记option不带参数时
      • 待匹配值设为:
      • $OPTARG设为option
    • option无效时
      • 待匹配值设为?
      • $OPTARG设为option
  • 函数内部处理函数参数,否则处理脚本参数
  • 参数标识-可省略