Bash 学习总结
志正则众邪不生,心静则众事不躁。
导航
壹-变量
1、变量管理
var=1:创建/更改变量unset var:删除变量echo $var:读取变量env/set:读取全部变量read var:从键盘读取变量export var:声明变量为全局变量declare [-aixr] var:定义变量类型为 数组、整型、全局变量(同 export 一样 )、只读。

var[1]=1;var[2]=b:创建数组变量;echo ${var[*]}:遍历数组变量。
注意: (1)默认情况下,Bash 中的变量属于万能变量,什么类型的值都可以被赋予一个变量。(2)变量的创建/更改不需要加
$符号,只有在读取变量的时候才需要在变量名前面加$符号。
2、默认变量
$?:保存上一条命令的执行是否成功的状态。$#:代表脚本名或函数名后面接的参数的个数,例如script_name v1 v2 v3 v4则表示为 4$@:代表"$1" "$2" "$3" "$4"多个变量之意,每个变量都是独立的(相当于一个包含多个变量的数组变量)。${*}:代表"$1 $2 $3 $4"一个变量之意, 这个字符变量的值为此这些(相当于一个字符串变量)。$LINENO:显示脚本中每行的行号。$RANDOM:产生一个随机数值。randomString =$(tr -dc A-Za-z0-9 </dev/urandom | head -c 8 ; echo ''):产生一个随机字串。
注意:
$@与$*基本上还是有所不同,一般情况下还是推荐使用$@为好。 有点怪异的是$@与"$@"的输出结果并不一样,当你传入的参数内带有双引号时,建议还是使用"$@"来带入脚本中,否则双引号会被取消,这样执行结果的差异会相当大!尤其是像./script one "a to b"这种仅有两个参数的且参数内还带有空格的,最容易出现莫名其妙的问题!
3、字串内调
- 删除:
#删除从左到右匹配到的最短的内容;##删除从左到右匹配到的最长的内容;%删除从右到左匹配到的最短的内容;% %删除从右到左匹配到的最长的内容;【例如,echo ${PATH%%:*bin}将 PATH 变量中匹配到的最长字段删除;echo ${PATH%???}删除变量末尾的 3 个字符。】 - 替换:
/替换匹配到的第一个旧字符串为新字符串;//替换匹配到的所有旧字符串为新字符串;【例如,echo ${PATH//sbin/SBIN}将 PATH 变量中所有小写的 sbin 替换为大写的 SBIN。】 - 赋值:赋予未定义的变量为指定的值,详情见下表。【例如,
username =${username-root}先判断 username 是否未定义,若未定义则赋值为 root,若已定义或值为空,则保留原值。】

4、作用范围
(1)进程之间的变量(不同进程)
在父子关系的程序中,变量可否被引用与 export 有关。被 export 后的变量我们称其为环境变量,父程序的环境变量可以被子程序所引用,而其它未被 export 的变量就不会存在于子程序中。
那么为什么环境变量的值可以被子程序所引用呢?这是因为内存分配的原因。具体流程如下:
- 当启动一个 shell,操作系统会在内存中为这个 shell 分配一块静态存储区和栈,以供 shell 使用。
- 若父程序使用 export 时,可以让自定义变量(栈)的内容写到静态存储区(环境变量)当中。
- 当加载另一个 shell 时(亦即启动子程序,离开原来的父程序),子 shell 可以将父 shell 中的环境变量导入自己的环境变量区块中。
透过这样的关系,我们就可以让变量在存有父子关系的进程之间互用了。 不过需要注意的是,环境变量与 bash 的操作环境的变量不太一样。如:PS1 并不是环境变量,但是这个 PS1 会影响到 bash 的接口。(在脚本中写入 read 读取键盘时,此时显示的 shell 应该是子 shell,但是我们发现子 shell 的 bash 操作环境与父 shell 的操作环境似乎是同一个,故子 shell 操作环境是一个很特殊的环境,它的形成可能并不需要读取配置文档。)
(2)函数之间的变量(同一进程)
在同一个 shell 进程中,在函数中定义变量且该函数已被调用,而在该函数中定义的变量并不会随着函数的执行结束而被回收,它依然存在于进程的变量环境中可被其它函数调用。【注:由此推断,shell 中没有变量回收的功能。所以为了防止函数变量的混乱,可以适当在函数结束的时候通过 unset 来删除那些仅需存在于函数内部的变量。】
5、注意事项
- 变量类型默认是字串,所以若不指定变量类型为整型,则
var = 1+2是一个字符串而不是计算表达式。若不想指定变量的类型为整型,则可用$((${a}*${b}))替代declare -i total=${a}*${b}。 - Bash 环境中的数值运算,结果精度最大只能到整数,所以
1/3的结果只能是 0。而若想要让输出结果更准确,则需要借助 bc 这个指令来实现。例如:echo "scale=6;1/3"|bc -lq可以让结果达到小数点后 6 位。【注:scale 就是指定计算结果将精确到小数点后几位的意思。】
贰-流程控制
1、条件控制
(1)单层、简单条件判断式
注:如果上述语法中 then 单起一行,那么 if 后面就不用再有分号了。
(2)多重、复杂条件判断式

(3)利用 case 条件多选判断式
2、循环控制
(1)不定循环 while、until
注:while 是当条件为真时执行循环,直到条件为假才停止循环,until 是直到条件为真才停止循环。两者适用范围各有千秋,不过一般 while 更常用些。
(2)固定循环 for
注:(1)for...in... 语法中的字串列表可以是变量
$a(其中 a 的值是:a = "a b c")、命令结果$(seq 1 10)/$(cut -d ":" -f1 /etc/passwd),只要输出结果中各字符串之间是用空格分割的即可;(2)for (( )) 语法与 C 语言中的用法一致,如:for (( i = 1; i < 100; i++ ))。
3、条件表达式
- 表达式语法
[ expersion ]等价于命令test expersion,虽然[ expersion ]用法简洁但要求严格,有时候少写一个空格分隔、变量不加双引号都会引发语法错误。 - 单独使用命令
test expersion判断时,执行结果并不会显示任何讯息。此时,可通过$?/&&/||来间接判断结果。例如:test -e /test && echo "exist" || echo "not exist" - 中括号中的判断式可以这样互相转换而不影响执行:
[ "${yn}" == "Y" -o "${yn}" == "y" ]等价于[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]。 - if 后面的条件可以是表达式也可以是命令,但是针对表达式和针对命令的行为是不一样的。命令执行成功返回的值是 0,而 0 对于表达式来说意味着假,但对于命令来说它却代表真。【例如:
if echo hello; then echo "命令执行成功";fi,echo hello;if [[ $? == 0 ]]; then echo "命令执行成功";fi】【注意:这一区别请务必要注意了。详细参考】
表达式所支持的判断用法:

叁-函数
1、函数语法
注意:由于脚本是自上而下、自左而右的执行方式,因此在 shell 脚本中的 function 的设定一定要在程序的最前面(即被调函数的前面即可),这样才能够在执行时找到对应的执行函数。
2、函数变量
- 和脚本默认变量一样,在函数中也有关于函数的默认变量,即
$0 $1 $2这些变量。但这些变量只能作用于每个函数自身,这也就意味着,对应于脚本的$0 $1 $2变量是不能够直接被函数使用的,而是需要通过中间变量来传递。 - shift n 可以造成参数变量的偏移:如 shift 2 就表示将上例变为 opt1 opt2 opt3 opt4,拿掉了两个参数变量。
注意:在 bash 中的函数调用是
func v1 v2这样的,而不是func(v1,v2)这样的。
肆-杂项
(1)脚本首行的 #! /bin/bash 是用来声明该脚本应该需要使用哪个类型的 shell 才能够被完美执行,虽然不是必需要有,但一般还是指定为好。【注:这是因为不同 Linux 版本默认的 shell(Bash、Csh、Tcsh、Sh)可能并不相同,而不同 shell 之间的语法又有着些许的差别,因此,在bash 中可以被运行的脚本,在 sh 中可能就不一定能够正常运行了。】
(2)在 shell 中执行命令时,如果命令很长,则可以通过跳脱符号的方式,将一行命令拆分成多行去输入。
for x in 1 2 3 \
; do \
echo $x \
; done
#以上等价于 for x in 1 2 3; do echo $x; done
#而不能是
for x in 1 2 3 \
do \
echo $x \
done
#以上等价于 for x in 1 2 3 do echo $x done
#注意,这是语法错误,因此要使用这种多行撰写命令的方式,一定要按照单行命令的方式去写,而不是按照 Bash 脚本的方式去写。
(3)执行脚本有三种方式:(1)直接执行则需要用户对脚本具有 rx 权限,然后才能 ./shell.sh。(2)Bash 执行则只需要用户对脚本具有 r 权限即可,然后才能 bash shell.sh。(3)另一种是 source ./shell.sh,需要用户对脚本具有 r 权限。
注意:这前两种执行脚本的方式是在一个新衍生的 shell 进程中执行脚本中的内容,而这第三种则是在不衍生新 shell 进程的情况下,将脚本中的内容模拟手动执行命令的方式在当前 shell 中执行。见下图:
(4) Shell 脚本 Debug
#仅检查语法问题,不执行
sh -n test.sh
#将脚本的执行过程详细呈现出来
sh -x test.sh
(5)shell 中管道符两侧的命令/进程将同步运行,只不过是通过管道在互相通信。(fork 炸弹即是根据此特性实现的进程指数级增加,从而耗尽系统的内存资源导致系统宕机。)
(*)Shell 中的通配符与特殊符号

