掌握这篇 shell 技巧,就能应付 80% 的测试场景需求了
引言
都 21 世纪 20 年代了,为什么还是少不了远古时期的 shell script ?Python 那么火,难道不香吗?
对于我这样一个左手 Shell 右手 Python 的人来说,只能说这个真的跟工作任务的场景有关。
特别是软件测试工程师,不同于软件开发工程师,不需要像开发工程师那样对一门编程语言的极致精通,测试工程师的职责是保障产品质量,工具(包括编程语言)的使用是为了更便捷更高效的辅助工作,所以当工作场景需要在 Unix/Linux 平台上进行各种操作的时候,Shell 作为直接跟系统内核打交道的命令语言有着天然的优势和便利,硬要用 Python 不是为难自己么?
事实上,我写下这篇的原因和动力,正是我那些测试工程师同事们的需求。
从哪里开始
Shell 命令其实非常多,不过如果我们不是系统运维,而是作为一个 Linux 环境上的测试工程师而言,掌握 20 个左右就足够了。
其中有大部分是基础中的基础,只要用 Linux 就会用到,所谓的高级一点的用法无非是编程,或者是命令与命令之间用管道符连接组成复杂的长命令。
基础命令
这些命令多是对磁盘/目录/文件的操作,单独使用非常简单。
命令 | 说明 | 常用示例 |
---|---|---|
ls |
列出目录下的所有文件/目录 |
ls ls -l 参数 -l 是以列表方式显示ls -la 参数-a 是显示隐藏文件/目录 |
cd |
切换目录 |
cd /root 切换到 root 目录下cd ~ 切换到当前用户的 home 目录 |
pwd |
查看当前所在目录的绝对路径 | pwd |
mkdir |
创建目录 |
mkdir will_dir 在当前目录创建一个 will_dir 子目录mkdir -p will_dir/a/b/c/d 参数-p 使得不存在的目录自动创建 |
touch |
修改文件/目录的时间属性 |
touch will_file 如果 will_file 存在,就会修改时间属性,如果不存在,则会生成一个 will_file 的空文件 |
cp |
复制文件/目录 |
cp will_file will_file_new 复制 will_file 为 will_file_newcp will_file /tmp/ 复制 will_file 到 /tmp 目录下 |
mv |
移动文件/目录 |
mv will_file will_file_mv 修改 will_file 为 will_file_mv mv will_file_mv /tmp/ 移动 will_file_mv 到 /tmp 目录下 |
rm |
删除文件/目录 |
rm /tmp/will_file_mv 删除 /tmp/will_file_mvrm -rf will_dir 参数 -rf 是强制删除目录下的所有文件和目录 |
cat |
输出文件内容到屏幕 |
cat /etc/hosts 输出 /etc/hosts 里的内容 |
less |
分段加载显示文件内容 |
less /var/log/syslog.1 会先显示一屏幕的日志,支持上下滚动,可敲/ 进行搜索,按q 退出 |
wc |
用于计算字数 |
wc -l /etc/hosts 参数-l 会输出该文件的行数 |
grep |
查找文件中符合条件的字符串 |
grep localhost /etc/hosts 输出文件中包含 localhost 的行内容grep -v localhost /etc/hosts 输出文件中不包含 localhost 的行内容grep -A 1 localhost /etc/hosts 会输出文件中包含 localhost 的行以其后1行的内容grep -B 1 localhost /etc/hosts 会输出文件中包含 localhost 的行及其前1行的内容 |
head |
输出文件的头几行 |
head -n 2 /etc/hosts 参数-n 2 指定输出头 2 行 |
tail |
输出文件的末几行 |
tail -f /etc/hosts 参数-f 循环读取输出,靠ctrl+c 终止tail -n 2 /etc/hosts 参数-n 2 指定输出末 2 行 |
还有一些命令是查看系统资源,也是会经常使用到的。
命令 | 说明 | 示例 |
---|---|---|
ps |
显示当前进程状态 |
ps aux 参数aux 详细显示所有用户的进程 |
free |
显示内存状态 |
free -h 参数 -h 以合适的单位显示内存使用情况 |
top |
实时显示进程动态、CPU、内存 |
top 实时显示进程动态,按q 退出top -b -n 1 参数 -b 和-n 搭配使用,输出一次进程状态 |
df |
显示磁盘使用情况 |
df -h 参数 -h 以人类可读的方式显示 |
混搭
除了以上基础命令,还有一些很常用的命令,比如 echo
用来输出字符串,sed
和 awk
都是用来处理文件内容的,在多命令混搭或写脚本的时候非常有用。
这些命令单独看的时候,其实没有什么难度,主要是在想获取某种效果的时候需要混搭,需要靠 |
或>
或 >>
连接,比如:
效果 | 混搭命令 |
---|---|
/etc/hosts 中包含 localhost 字符的行数 | grep localhost | wc -l |
把 185.199.108.153 QualitySphere.github.io 添加到文件末尾 |
echo '185.199.108.153 QualitySphere.github.io' >> /etc/hosts 注意 >>
|
输出 /etc/hosts 的时候 github 改为 gitee 显示 |
cat /etc/hosts | sed 's/github/gitee/g' |
获取到文件中 QualitySphere.github.io 的 IP | grep github /etc/hosts | awk '{print $1}' |
对于相对复杂的查询获取,只要你愿意,可以继续用管道符|
一直传递下去,从而达到预期目标。
这其中相对较难的是 sed
和 awk
,如果对上面的例子还是没有概念的话,继续看以下两段:
sed
用得最多的替换输出或者文件中的字符串,比如要上面的例子,要替换 /etc/hosts 中我们添加的 QualitySphere.github.io 这行的指定内容:
grep QualitySphere /etc/hosts | sed 's/github/gitee/g'
这在输出的时候就把 github 字段换成了 gitee,格式为 's/string_a/string_b/g'
,s
是告诉 sed
我们要进行替换操作,g
是替换所有的 string_a
,如果没有g
就只替换搜索到的第一个。
有时候我们的需求是直接修改一个文件中的目标字符串,使用 sed
的时候加上 -i
参数,比如 sed -i 's/github/gitee/g' /etc/hosts
执行之后,该文件内容直接被修改了,不过值得注意的是,如果是在 Mac 上执行该命令,通常 Mac 默认的 shell 是 zsh,与 Linux 默认的 bash 不太一样,-i
后面要带个值,空值就好,sed -i '' 's/QualitySphere/qsphere/g' /etc/hosts
要想删除该行其实是有 d
参数的,不过为了减少记忆,依旧可以简单粗暴的用替换的方法,因为sed
支持正则表达式,因此使用 sed -i 's/.*qsphere.*//g' /etc/hosts
也能达到效果。
awk
则是更是超级强大,甚至有很多 AWK 编程实战的技术贴,没错,这货是个程序设计语言,我有幸体验过它性能的强大。
不过通常我们比较常用的是通过它来获取指定目标位置的字符串, 比如我们去获取内存的值,free -h | grep Mem | awk '{print $2}'
首先会 grep
出内存那一行,如果以空格为分隔符,我们会看到内存的值是在第 2 列,因此用 '{print $2}'
来获取第 2 列的值。
有时候分隔符只用空格是肯定不行的,比如我要获取一个 IP 的最后一个数值,可以指定.
为分隔符,echo "192.168.1.199" | awk -F '.' '{print $4}'
参数 -F
指定分割符,然后获取第 4 个值。
脚本
很多时候光有命令还是不够的,总要写一些脚本,把重复的事情交给机器。
通常,循环和逻辑判断就已经够用了,高阶用法无非是高效且优美。
语法 | 说明 |
---|---|
if ...; then ...; else ...; fi |
判断语句 |
for ... in ...; do ...; done |
for 循环 |
while ...; do ...; done |
while 循环 |
举些例子,注意其中的符号及空格的细节。
判断两个字符串
A=will
B=bxwill
if [[ "$A" == "$B" ]]; then # 字符串用 "..." 括起来,用 [[ ... ]] 来判断
echo PASS
else
echo FAIL
fi
判断两个数字
A=1
B=2
if (( $A == $B )); then # 数字不需要 "...",用 (( ... )) 来判断
echo PASS
else
echo FAIL
fi
还有一些逻辑判断很常用
if [[ -f /etc/hosts ]]; then # 判断文件是否存在
echo PASS
else
echo FAIL
fi
if [[ -d /etc ]]; then # 判断目录是否存在
echo PASS
else
echo FAIL
fi
A=will
if [[ -z "$A" ]]; then # 判断字符串是否为空
echo PASS
else
echo FAIL
fi
if [[ -n "$A" ]]; then # 判断字符串是否不为空
echo PASS
else
echo FAIL
fi
for 循环
for i in `seq 1 10` # seq 1 10 直接生成 1-10 的数字,用键盘左上角的 ` 符号括起来,会先执行该命令
do
echo $i
done
while 循环
while true # 看,这是一个死循环
do
sleep 1 # 停顿 1 秒,在脚本中也是很常用的命令
echo PASS
done
其他技巧
最实用的莫过于后台执行了,比如你写了一个脚本叫 check_me.sh
,想丢在后台执行,nohup ./check_me.sh &
它就会使该脚本在后台执行,并且日志会存在当前目录的 nohup.out 中。
当然,丢后台执行那是写好了脚本,最伤心的事情莫过于,你正在调试,敲了一堆命令,坐等运行结果的时候,突然断网,呜呼哀哉,一夜回到解放前,Linux 自带 screen
工具,连接到终端准备大展拳脚之前,顺手输入 screen
开启一个视窗,若在一阵敲打过程中突然掉线,莫方,再次登录终端,尝试 screen -ls
一下,你会看到之前开启的视窗在列表中,开头的那窜数字就是它的 ID,screen -r $ID
就又回到之前的视窗,所有的操作又回到了你的眼前。
同样的效果有个更不错的工具 tmux
,则需要自行安装,它比 screen
更友好一些,可自行了解。
文中未涉及到的用法和命令,感兴趣的可以去搜索一番,或者直接在命令行用
man
命令来学习。