Bash 的高级特性
bash支持很多强大的功能,还可以用脚本编程,这也体现了Linux的一些设计理念。熟练后,你会觉得Linux很简单,很方便,它对你是开放的,没有那么多的限制。比如说重定向和管道,可以让你通过一个小小的操作符控制命令的输出和输入流。另一方便可能它是hack设计出来的系统,有些表达比较专业,开始可能有点匪夷所思。让我们来更深入的学习一下 bash。
重定向
什么是重定向?简单说就是将命令默认的输入输出方式,改变成另外一种输入输出方式,比如说 一般命令将结果输出到屏幕,你可以通过重定向将结果输出到文件。命令的默认输入输出是什么呢?Linux默认的三个输入输出设备:
标准输入 (stdin) 代码为 0
标准输出 (stdout) 代码为 1
标准错误输出(stderr) 代码为 2
那个代码其实就是文件操作符,前面说了Linux所有设备都是文件,输入输出也不例外,打开后返回文件描述符。文件描述符就是数字,操作系统能够通过这个数字找到相应文件或设备。0,1,2被它们三个占用了,后面文件从3开始递增生成文件描述符。执行一个命令,默认输入是标准输入(如果需要输入),过程中默认会把正常的信息输出到标准输出 (stdout),把错误信息输出到标准错误输出(stderr)。默认情况下标准输出和标准错误输出都指屏幕。
这是Linux默认行为,如果你想改变你就可以使用重定向。比如从文件内容输入,把错误信息输出到文件等等。Linux定义了重定向符号:
- 标准输入 (stdin) :代码为 0 ,使用 < 或 <<, < 表示重定向输入 << 设置终止符号
- 标准输出 (stdout):代码为 1 ,使用 > 或 >>, > 表示覆盖 >> 表示追加
- 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>>, 2> 表示覆盖 2>> 表示追加
演示一下:
#默认输出到屏幕
[root@localhost ~]# ls -l
total 8
-rw-------. 1 root root 1208 Sep 13 10:23 anaconda-ks.cfg
-rw-r--r--. 1 root root 26 Sep 18 16:37 test.txt
#将标准输出重定向到ls.out 文件,如果文件不存在会自动新建,如果存在则清空后在写入。
[root@localhost ~]# ls -l > ls.out
[root@localhost ~]# cat ls.out
total 8
-rw-------. 1 root root 1208 Sep 13 10:23 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Sep 19 16:46 ls.out
-rw-r--r--. 1 root root 26 Sep 18 16:37 test.txt
#可以再以追加的形式重定向,执行两次
[root@localhost ~]# ls -l >> ls.out
[root@localhost ~]# ls -l >> ls.out
#文件里面有三次ls的执行结果
[root@localhost ~]# cat ls.out
total 8
-rw-------. 1 root root 1208 Sep 13 10:23 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Sep 19 16:46 ls.out
-rw-r--r--. 1 root root 26 Sep 18 16:37 test.txt
total 12
-rw-------. 1 root root 1208 Sep 13 10:23 anaconda-ks.cfg
-rw-r--r--. 1 root root 166 Sep 19 16:46 ls.out
-rw-r--r--. 1 root root 26 Sep 18 16:37 test.txt
total 12
-rw-------. 1 root root 1208 Sep 13 10:23 anaconda-ks.cfg
-rw-r--r--. 1 root root 333 Sep 19 16:48 ls.out
-rw-r--r--. 1 root root 26 Sep 18 16:37 test.txt
#myfile不存在,会报错
[root@localhost ~]# ls myfile
ls: cannot access myfile: No such file or directory
#重定向到标准输出
[root@localhost ~]# ls myfile > out
ls: cannot access myfile: No such file or directory
#可以看到标准输出重定向的文件没有内容,那个错误信息是标准错误输出
[root@localhost ~]# cat out
#输出到错误输出
[root@localhost ~]# ls myfile > out 2>err
[root@localhost ~]# cat err
ls: cannot access myfile: No such file or directory
#还有一个常用的形式就是将错误输出也输出到标准输出,只用一个重定向文件
[root@localhost ~]# ls myfile > out 2>&1
[root@localhost ~]# echo "input test" > input.txt
#把文件重定向到标准输入
[root@localhost ~]# read input < input.txt
[root@localhost ~]# echo $input
input test
连续命令执行
有些时候我们想一次执行多个命令,命令之间可能还会有依赖,这些需求bash都能提供支持。
分号连接使用格式: cmd1;cmd2;cmd3。这种方式可以实现依次执行多条命令,命令直接没有任何相互依赖和影响。
演示示例:
#创建完目录后,进入目录创建一个文件myfile
[root@localhost mydir]# mkdir /data/mydir;cd /data/mydir;touch myfile
细心的朋友可能会说目录创建失败,后面命令能执行成功吗?整个操作可控吗?的确这里没有考虑容错处理,没有根据mkdir命令的结果来做相应的措施。
bash 还支持两个连接符 && 和 ||
&& 表示如果前面命令执行成功了,才执行后面的命令,否则后面的命令不执行;|| 表示如果前面命令失败了,就执行后面的命令,否则不执行。怎样判读成功是吧?Linux约定命令返回值为0表示成功,其它表示失败。可以通过 $? 变量获取返回值。
修改一下上面的命令:
[root@localhost mydir]# ls /data/mydir || mkdir /data/mydir && cd /data/mydir &&touch myfile
先用ls判断一下目录是否存在,不存在ls命令会报错,这时会执行mkdir命令;如果存在则不会执行mkdir命令直接跳到后面执行,成功跳转目录后才会执行touch命令。
连接符是从左往右结合的,|| 符号,执行一个另一个跳过,将返回值向后面传递。
管道
可以方便帮你实现流水线式作业,可以简单理解为它是重定向和 && 的结合体。使用方式:cmd1 | cmd2 | cmd3 。把多个命令用竖线连接起来,达到的效果就是 cmd1.stdout ----> cmd2.stdin ----> cmd2.stdout ----->cmd3.stdin---->cmd3.stdout 。只要有一个命令失败,整个操作就停止,整体操作失败。
这就要求 这些命令要支持 标准输入和标准输出,错误输出会被忽略。
演示一下:
#查找文件中包含network 的行
[root@localhost ~]# cat anaconda-ks.cfg | grep network
network --bootproto=dhcp --device=enp0s3 --ipv6=auto --activate
network --hostname=localhost.localdomain
常用命令
cut
"切"名字很形象,就是将列文件中选取出部分列,不会修改原文件,只是提取出一部分。用法:
[root@localhost ~]# cut --help
Usage: cut OPTION... [FILE]...
Print selected parts of lines from each FILE to standard output.
Mandatory arguments to long options are mandatory for short options too.
-b, --bytes=LIST select only these bytes <===按字节长度选择
-c, --characters=LIST select only these characters <===按字符长度选择
-d, --delimiter=DELIM use DELIM instead of TAB for field delimiter <===指定分隔符
-f, --fields=LIST select only these fields; also print any line
that contains no delimiter character, unless
the -s option is specified
-n with -b: don't split multibyte characters
演示一下:
#/etc/passwd一共7列,用冒号分开,选择第一列和第七列
[root@localhost ~]# cut /etc/passwd -d ':' -f 1,7
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
shutdown:/sbin/shutdown
halt:/sbin/halt
.....
#可以结合管道使用
[root@localhost ~]# cat /etc/passwd | cut -d ':' -f 1,7
#选择前四个字符
[root@localhost ~]# cat /etc/passwd | cut -c 1-4
这个命令经常用于简单日志数据处理,如果文件数据不是特别规则,比如多个分隔符 处理起来比较麻烦,可以考虑其它命令
sort
在处理和分析数据时,可能会用到排序,sort 命令支持 按类型,按指定列排序,主要命令参数:
sort [-fbMnrtuk] [file or stdin]
选项与参数:
-f :忽略大小写的差异,例如 A 与 a 视为编码相同
-b :忽略最前面的空格符部分
-n :使用数字类型进行排序(默认是以字符序)
-r :反向排序
-u :就是 uniq ,相同的数据中,仅出现一行代表
-t :分隔符,预设是用 [tab] 键来分隔
-k :以哪个列来进行排序
演示一下:
#按照第一列用户名升序
[root@localhost ~]# sort -t ':' -k 1 /etc/passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin
avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
hainiu2:x:1003:100::/home/hainiu2:/bin/nologin
hainiu:x:1001:100::/home/hainiu:/bin/bash
halt:x:7:0:halt:/sbin:/sbin/halt
.....
#按字符id排序,可以看出不是想要的数字大小结果,对的 默认是字符序
[root@localhost ~]# sort -t ':' -k 3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
hainiu:x:1001:100::/home/hainiu:/bin/bash
hainiu2:x:1003:100::/home/hainiu2:/bin/nologin
ttt:x:1004:1004::/home/ttt:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
#这回对了
[root@localhost ~]# sort -t ':' -k 3 -n /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
uniq
uniq 实现排重,重复列只显示一行。排重之前必须排序,否则结果不是你想要的,所以一般是结合sort命令一起用。
我们看看一个综合例子:统计/etc/passwd中配置为默认shell的前5名shell。写写试试:
[root@localhost ~]# cat /etc/passwd | cut -d ':' -f 7 | sort|uniq -c|sort -n -t ' ' -k 1 -r|head -5
18 /sbin/nologin
3 /bin/bash
1 /sbin/shutdown
1 /sbin/halt
1 /bin/sync
体会一下管道的魅力,先提取出默认shell那列数据;然后排序,排重计数;最后对计数结果排序;最终取出前5条
wc
帮你统计文件的行数,单词数和字节数。一个wordcount命令:
[root@localhost ~]# cat /etc/passwd| wc
25 47 1202
#只显示行
[root@localhost ~]# cat /etc/passwd| wc -l
25
tee
双向重定向输出,数据上面讲了要么输出到屏幕,要么重定向到别的设备,能不能继续输出到屏幕,同时再输出到其它设备一份?可以的tee就是实现这个双向输出。
演示一下:
#输出屏幕同时,输出到文件output
[root@localhost ~]# cat /etc/passwd| tee output
#追加的方式输出到文件
[root@localhost ~]# cat /etc/passwd| tee -a output