trick command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 获取当前文件绝对路径
curPath=$(cd `dirname $0`; pwd)
# 判断当前用户是否为root用户
user=$(env | grep USER | cut -d "=" -f 2)
# 字符串A包含字符串B
if [[ "$A" =~ "$B" ]]
# ${var##*/}字符串截取
# 使用正则提取子串
echo here365test | sed 's/.*ere\([0-9]*\).*/\1/g' # 输出365
# 其中s表示替换,\1表示用第一个括号里面的内容替换整个字符串, sed支持*,不支持?、+,不能用\d之类,正则支持有限。
# 变量默认值 如果a有变量就是a的值,否则是1 同理${a:--1}才是-1
${a:-1}
# 判断变量是否存在, 如果变量var存在就输出var,否则输出"",和${var:-}的区别在于,var不存在前者不会报错,后者会报错
if [ -z/-n ${var:+} ]; then
fi
# 文件按行读出
常规可能使用for line in `cat xx`,但是这样的问题在于如果一行中有空格则会变成多个变量进入循环,所以请使用
cat xx | while read line
do
echo ${line}
done
其实read非常实用,除了它本身的功能,最好用的就是循环赋值了,上面也展示了,当然还可以为多个变量同时赋值,例如awk获取了两个字段
read v1 v2 <<< `cat xx | awk '{print $1, $2}'`
空格会将一行拆分成多行正是因为系统默认分隔符IFS(内部字段分隔符internal field separator),其默认是空格,\t或\n。所以如果我们修改IFS也能达到按行拆分的效果。好处在于“我在go调用cmd命令时read命令不起作用!”
OLD_IFS="$IFS"
IFS=$'\n'
xxxxx
IFS=$OLD_IFS
其中首尾两行用于恢复默认IFS,如果不需要也可以不写
字符串分割使用read也非常方便, 可以通过IFS自定义分隔符,其中IFS在while scope内部定义,也不会影响到全局的IFS
-r 会对\n等转义字符保留\,-a表示作为数组读入
while IFS="$delimiter" read -ra readLines; do
for line in "${readLines[@]}"; do
xxx
done
done <<< "$string"
# 遍历文件夹下所有文件,但是带有空格,和上面的一样都是空格在捣乱,这里可以使用和上面一样的方法,也可以使用先替换再替换回来的方法
symbol="觉d怼e部z科k恁" # 选择一个绝对不可能出现的短语
for file in `ls xx | sed 's/ /'"${symbol}"'/g'`
do
realFileName=`sed 's/'"${symbol}"'/ /g' <<<$file` # realFileName就是包含空格的真正文件名
done
# 嵌套变量可使用eval读出, 用于传入一个环境变量本身的名字,然后获取环境变量的真实值
a="cat"
b="a" # 传入b
eval tmp=`echo '$'$b` # eval会多次扫描,第一次->echo $a,第二次->cat
echo $tmp # 结果就是cat
关于更多eval:https://www.cnblogs.com/huzhiwei/archive/2012/03/14/2395956.html
关于shell的并发和管道控制 https://blog.csdn.net/dubendi/article/details/78931979
对于自动化或者测试来说,我们在执行程序时可能会需要键盘输入,使用expect可以很方便地通过判断程序输出来控制输入,实现自动化。
https://www.jellythink.com/archives/373
shell 编程
- 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。1 2 3 4 5 6 7
array_name=(value0 value1 value2 value3) # 获得数组所有元素 ${array_name[@]} 或 ${array_name[*]} # 获得数组长度 ${#array_name[@]} # 取得数组单个元素的长度 lengthn=${#array_name[n]}
- 循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 步进
{1..10} 或 $(seq 1 10)
{1..10..2} 或 $(seq 1 2 10) # 步长为2
# 其中所有;do都改成下行写do不要;
# for 循环
for 变量名 in 列表;do
循环体
done
# whlie 循环
while CONDITION; do
循环体
done
- 参数
1
2
3
4
5
6
7
8
9
10
11
12
$0 Shell本身的文件名
$1 Shell的第一个位置参数,一直到$9;当n>=10时,需要使用${n}来获取参数
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
最后一个参数不能使用${$#},需要使用${@: -1},并且中间的空格是必须的,或者是${!#}。
但是如果没有参数,那么第一种是空串,而第二种则是相当于$0,也就是脚本名,所以推荐使用第一种。
- 比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 字符串比较(前六个也可用于数字比较)
-eq 等于
-ne 不等于
-gt 大于
-lt 小于
-le 小于等于
-ge 大于等于
-z 空串
= 两个字符相等
!= 两个字符不等
-n 非空串
=~ 左边字符串包含右边字符串
# 文档比较
-e filename存在
-d filename为目录
-f filename常规文档
-L filename为符号链接
-r filename可读
-w filename可写
-x filename可执行
bash编程常用技巧
bash严格模式,将这两行放在开头
1
2
3
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
bash script执行加锁
1
2
3
4
5
if ! (set -o noclobber ; echo > /tmp/global.lock) ; then
exit 1 # the global.lock already exists
fi
# ... remainder of script ...
冒号的妙用
在linux中冒号(:)常用来做路径的分隔符(PATH)。其实,冒号(:)在Bash中也是一个内建命令,它啥也不做,是个空命令。
常见作用如下
1、占位符
比如在编写脚本的过程中,某些语法结构需要多个部分组成,但开始阶段并没有想好或完成相应的代码,这时就可以用:来做占位符,否则执行时就会报错。
1
2
3
4
5
if [ "today" == "2011-08-29" ]; then
:
else
:
fi
2、写注释,但是一般不这样写,就不举例了
3、清空文件
:>filename
4、缺省值
echo ${var:-DEFAULT}
https://www.cnblogs.com/ChinaGo/p/9910747.html
awk
awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息
特殊要点:
$0 表示整个当前行
$n 每行第n个字段
NR 每行的记录号,多文件记录递增
…
分隔符:
1
2
3
awk -F":" '{print $1}' filename 以:为分隔符输出每行分割后第一项
awk -F: '{print $1; print $2}' filename 将每一行的前二个字段,分行输出
awk -F: '{print $1,$3,$6}' OFS="\t" filename 输出字段1,3,6,以制表符作为分隔符
判断语句:
1
2
3
4
5
6
awk -F: '{if($1~/mail/) {print $1} else {print $2}}' filename
awk -F: 'NR==5{print}' filename 显示第5行
其中匹配方法有:
//纯字符匹配 !//纯字符不匹配 ~//字段值匹配 !~//字段值不匹配 ~/a1|a2/字段值匹配a1或a2
条件表达式:
== != > >= > <
需要使用shell中的变量只需要使用-v参数,例如shell中有一个参数num=2
awk -v inner_num=${num} 'NR==inner_num{print $1}'
,但是需要注意,如果num中包含/符号则会报错,需要用双引号括起来
sed
https://www.cnblogs.com/maxincai/p/5146338.html
jq
详细文档可以看https://stedolan.github.io/jq/manual/
说一个用例,获取一个数组中每个元素的id,并生成bash中的array
1
2
3
4
5
6
# @sh => 输入经过转义,适合在 POSIX shell 的命令行中使用。如果输入是数组,则输出将是一系列以空格分隔的字符串。
# tr -d 删除多余的引号,这个视情况而定,字符串可以不删除引号
array=($(yabai -m query --displays | jq '.[].index | @sh' | tr -d \'\"))