Bash 的高级特性实践总结

分享 青牛 ⋅ 于 2016-12-05 19:13:11 ⋅ 最后回复由 青牛 2016-12-25 02:14:57 ⋅ 3969 阅读

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
版权声明:原创作品,允许转载,转载时务必以超链接的形式表明出处和作者信息。否则将追究法律责任。来自海汼部落-青牛,http://hainiubl.com/topics/6
本帖由 青牛 于 6年前 解除加精
回复数量: 1
  • 青牛 海汼部落创始人,80后程序员一枚,曾就职于金山,喜欢倒腾技术做产品
    2016-12-25 02:14:57

    又学会了好多linux命令

暂无评论~~
  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,可用Emoji的自动补全, 在输入的时候只需要 ":" 就可以自动提示了 :metal: :point_right: 表情列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif,教程
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
Ctrl+Enter