Shell 学习笔记 0x07
1. 数组
Bash 支持一维数组,比如保存 O 中比较 😭 UOHHHHH 的角色:
1 | arr=(Yaoyao Nahida Dori Diona Sayu Klee Qiqi) |
还可以:
1 | arr=([0]=Yaoyao [1]=Nahida [2]=Dori [3]=Diona [4]=Sayu [5]=Klee [6]=Qiqi) |
或者:
1 | arr[0]=Yaoyao |
访问元素可以用下标:
1 | echo ${arr[5]} |
btw 可莉酱找我聊天了捏
正好是下午 6:48 打来的,但鼠鼠没钱捏,冲不起 648 ,最多冲个小月卡捏
1.1. 访问数组的元素
1 | arr=("00 Yaoyao" "01 Nahida") |
1.2. 数组里元素的个数
Bash 数组允许元素下标使用任何数字,比如:
1 | arr[114514]="foo" |
arr 数组只有一个元素,比如求数组元素个数:
1 | echo ${#arr[@]} |
不同于其他语言, Bash 不会对元素 0-114513 进行初始化,所以 Bash 也有获取数组有效下标的方法:
1 | arr=([1]="foo" [14]="bar" [514]="baf") |
1.3. 传递数组作为参数
Bash 不能传递数组,需要先 "${arr[@]}" 把数组变成参数字符串,然后用 $@ 接受这个字符串,也就是说变成了普通的位置参数,原来的下标也会失效:
1 | function print_arr () { |
1.4. 在数组末尾添加元素
通过 += Bash 可以自动把元素:
- 如果 0 没有元素则放进 0 里
- 否则放到末尾( 1 到最大下标之间的空位不会被填补)
1 | function print_arr () { |
输出:
1 | 2 3 4 |
1.5. 删除数组元素
删除一个变量可以用 unset :
1 | echo $foo |
unset 也可以用来删除数组的某个元素:
1 | arr=(foo bar baf) |
或者整个数组:
1 | arr=(foo bar baf) |
但光是 var= 不能删除整个数组:
1 | arr=(foo bar baf) |
1.6. 数组排序
Bash 没有内置的排序,但可以借助 sort :
1 | function sort_arr () { |
1.7. 关联数组
像普通数组相似,关联数组可以用字符串作为下标,类似 Python 的 dict ,关联数组必须用 declare -A 来声明:
1 | declare -A colors |
2. 其他
2.1. Group/Subshell
1 | ls /path/to/dir1 > files |
这种可以用 group 来实现:
1 | { ls /path/to/dir1; ls /path/to/dir2; ls /path/to/dir3; } > files |
用 { } 包围,且要用空格与指令隔开,每个指令都要用 ; 结尾。或者用 subshell :
1 | (ls /path/to/dir1; ls /path/to/dir2; ls /path/to/dir3) > files |
用 ( ) 包围,不需要用空格与指令隔开,最后一个指令也不需要加上 ; 。更重要的情况是使用 | 进行重定向:
1 | { ls /path/to/dir1; ls /path/to/dir2; ls /path/to/dir3; } | sort |
group 和 subshell 的区别顾名思义: group 里面的指令都在同一个 shell 里完成,而使用 subshell 会创建子进程、复制出一个 subshell 环境来执行指令,执行后这个子进程会被销毁,里面的变量也会同时被销毁:
1 | echo "bar" | { read foo; echo $foo; } |
| 右边读取 | 左边的输出、放进了变量 foo 里,且在 | 右边能正常读取 foo 变量,但指令结束后 foo 变量就被销毁了。一般情况下使用 group 占用资源更少、速度也更快
2.2. 进程替换
为了解决这个问题, Bash 有进程替换,一个用于产生标准输出 <(commands) 、一个用于接受标准输入 >(commands) ,比如上面的可以这样解决:
1 | { read foo; echo $foo; } < <(echo "bar") |
实际上 <(commands) 把 commands 的输出转换成了一个文件:
1 | echo <(echo "bar") |
2.3. trap
Bash 也可以捕获信号,类似 C 的 signal :
1 | trap "echo 'SIGINT, ignore'" SIGINT |
运行时会像这样:
1 | 1... |
trap 也可以真的像 signal 一样使用函数作为参数:
1 | function sigint_handler() { |
2.4. 临时文件
*nix 下,临时文件一般被放在 /tmp 目录下,比如按照 ${程序名}.${PID}.${随机数}
1 | tmp_file=/tmp/$(basename $0).$$.$RANDOM |
但 $RANDOM 只能随机 1-32767 之间的整数,有一个更简单的程序可以升级&简化这个操作:
1 | tmp_file=$(mktemp /tmp/$(basename $0).$$.XXXXXXXXXX) |
2.5. 异步执行
Bash 甚至有异步执行机制,但其实就是生成一个子进程但不阻塞父进程,然后可以等待子进程结束,其中子进程 PID 可以通过 $! 获得,比如子脚本 child.sh :
1 |
|
和父脚本 parent.sh :
1 |
|
运行父脚本:
1 | Parent: running... |
2.6. 命名管道
管道线 | 用的就是管道,用 mkfifo 可以创建一个命名管道,比如
1 | mkfifo pipe |
然后一边读取这个管道:
1 | cat < pipe |
另一边向管道输入内容:
1 | echo "foo" > pipe |
这样就模拟了:
1 | echo "foo" | cat |
未完待续
以后再来记录

