vlambda博客
学习文章列表

08.Linux学习——认识与学习 bash(3)

01
数据流重定向


数据流重定向就是将某个命令执行后应该要出现在屏幕上的数据传输到其他的地方,例如文件或者是设备。


1、什么是数据流重定向


一般来说,如果你要执行一个命令,通常它会如下图所示。



我们执行一个命令的时候,这个命令可能会由文件读入数据,经过处理后,再将数据输出到屏幕上。上图当中,standard output 标准输出指的是命令执行所回传的正确的信息。standard error output 标准错误输出可理解为命令执行失败后,所回传的错误信息。


当一次命令中,既有标准输出,也有标准错误输出时,可以通过数据流重定向将两者信息分别传送到其他的文件或设备去。分别传送所用的特殊字符如下:

  • 标准输入(stdin):代码为 0,使用 < 或 <<

  • 标准输出(stdout):代码为 1,使用 > 或 >>

  • 标准错误输出(stderr):代码为 2,使用 2> 或 2>>


02
stdout 和 stderr


#范例一:查看系统根目录,然后输出到指定的文件中root@iZ25nbozf5vZ:~# ll / <==此时屏幕会输出根目录下的文件信息root@iZ25nbozf5vZ:~# ll / > ~/rootfiles    <==此时屏幕无任何输出root@iZ25nbozf5vZ:~# ll ~/rootfiles <==有个u析文件创建了-rw-r--r-- 1 root root 1919 Mar 12 17:03 /root/rootfilesroot@iZ25nbozf5vZ:~# cat ~/rootfiles    <==通过cat查看文件内容就是原来屏幕本该输出的内容...


屏幕怎么会完全没有数据呢?这是因为原本“ll / ”所显示的数据已经被重新导向到 ~/rootfiles 文件中了。该文件的创建方式是:

  1. 该文件(本例中的~/rootfiles)若不存在,系统会自动将它创建起来;

  2. 当这个文件存在的时候,那么系统就会先将这个文件内容清空,然后再将数据写入;

  3. 也就是以 > 输出到一个已存在的文件中,那个文件就会被覆盖掉;

  4. 如果不想覆盖想以追加的方式输出,就用两个 >> 符号即可;

  5. 如果是 stderr 的错误数据就以 2> 或 2>> 输出即可。同理,上面的stdout也可以 1> 或 1>> 代替。注意数字 1,2 与 >,>> 中间没有空格。


如果一个命令既有正确数据输出又有错误数据输出时,可以通过以下方式将两种数据分别输出到不同的文件中:

root@iZ25nbozf5vZ:~# find /home -name .bashrc > list_right 2> list_error


如果只想在屏幕上输出正确数据,忽略掉错误的数据时可以通过以下方式:

root@iZ25nbozf5vZ:~# find /home -name .bashrc 2> /dev/null 


如果想将正确数据与错误数据写入同一个文件,可以通过以下方式中的一种:

root@iZ25nbozf5vZ:~# find /home -name .bashrc > list_file 2>&1root@iZ25nbozf5vZ:~# find /home -name .bashrc &> list_file


03
stdin


标准输入(standard input):< 与 <<,就是将原本需要由键盘输入的数据改由文件内容来替代。

#利用 cat 命令创建一个文件并通过键盘输入内容root@iZ25nbozf5vZ:~# cat > catfiletestingcat file test<==结束时按下 [ctrl+d] 离开输入
root@iZ25nbozf5vZ:~# cat catfile testingcat file test

由于加入 > 在 cat 后,所以文件 catfile 会被主动创建,而内容就是刚才键盘上面输入的那两行数据。


如果我们想用某个纯文本文件替代键盘的输入呢?也就是说,用某个文件的内容来替代键盘的敲击。可以通过如下方式:

root@iZ25nbozf5vZ:~# cat > catfile < ~/.bashrc root@iZ25nbozf5vZ:~# ll catfile ~/.bashrc <==查看两个文件-rw-r--r-- 1 root root 3106 Mar 12 18:14 catfile-rw-r--r-- 1 root root 3106 Apr 19  2012 /root/.bashrc#注意看,这两个文件的大小一模一样,几乎像是使用 cp 复制的一般


最后我们来看看 << 这个连续两个小于号,它代表的是结束输入的意思。就是说我们可以指定某个字符串结束此次输入而不需要按下 [ctrl+d] ,主要用于定shell脚本程序等场景下,如:

root@iZ25nbozf5vZ:~# cat > catfile << "eof" testingcat file testeof <==输入这关键词,立刻就结束而不需要按下 [ctrl]+d


04
命令执行的判断依据:;,&&,||


在某些情况下,很多命令我们想要一次输入去执行,而不想要分次执行时,该如何是好?基本上你有两个选择,一个是通过 shell script 编写脚本去执行,一种则是通过下面的介绍来一次输入多重命令。


  • cmd; cmd(不考虑命令相关性的连续命令执行)

在命令与命令中间利用分号(;)来隔开,这样一来,分号前的命令执行完成后就会立刻接着执行后面的命令了。

root@iZ25nbozf5vZ:~# ll /; ll /data/temp/


  • $?(命令回传码)与 && 或 ||

如果两个命令之间有依赖性,而这个依赖性主要判断的地方就在于前一个命令执行的结果是否正确。若前一个命令执行的结果为正确,在 Linux 下面会回传一个 $?=0 的值。那么我们怎么通过这个回传码来判断后续的命令是否要执行呢?这就要用到 "&&" 和 "||"。


命令执行情况 说明
cmd1 && cmd2

若 cmd1 执行完毕且正确执行($?=0),则开始执行 cmd2

若 cmd1 执行完毕且为错误($?≠0),则 cmd2 不执行

cmd1 || cmd2

若 cmd1 执行完毕且正确执行($?=0),则 cmd2 不执

若 cmd1 执行完毕且为错误($?≠0),则开始执行 cmd2


例如,我们想要在某个目录下创建一个文件,但是该目录不一定存在,我们需要先创建目录再在该目录下创建一个文件。

root@iZ25nbozf5vZ:~# ls /data/temp/abc || mkdir /data/temp/abc && touch /data/temp/abc/hehels: cannot access /data/temp/abc: No such file or directoryroot@iZ25nbozf5vZ:~# ls /data/temp/abc/hehe


05
管道命令(pipe)


bash 命令执行的时候有输出的数据会出现。那么如果数据必须要经过几道手续之后才能得到我们所想要的格式,应该如何来设置?这就牵涉到管道(pipe)命令了,管道命令使用的是 “|” 这个界定符号。另外管道命令与“连续执行命令”是不一样的。

root@iZ25nbozf5vZ:~# ls -al /etc/ | less


这个管道命令 “|” 仅能处理经由前面一个命令传来的正确信息,也就是 standard output 的信息,对于 standard error 并没有直接处理的能力。


在每个管道后面接的第一个数据必定是"命令",而且这个命令必须要能够接收 standard input 的数据才行,这样的命令才可以是“管道命令”,例如 less、more、head、tail 等都是可以接收 standard input 的管道命令。至于例如 ls、cp、mv 等就不是管道命令了。因为 ls、cp、mv 并不会接收来自 stdin 的数据。也就是说,管道命令主要有两个比较需要注意的地方了:

  • 管道命令仅会处理 standard output,对于 standard error output 会予以忽略。

  • 管道命令必须要能够接收来自前一个命令的数据成为 standard input 继续处理才行。


1、选取命令:cut, grep

什么是选取命令啊?说穿了,就是将一段数据经过分析后,取出我们所想要的,或者是经由分析关键字,取得我们所想要的那一行。不过要注意的是,一般来说,选取信息通常是针对“行”来分析的,并不是整篇信息分析的。


  • cut:这个命令可以将一段信息的某一段“切”出来,处理的信息是以“行”为单位。

[root@www ~]# cut -d '分隔字符' -f fields <==用于分隔字符[root@www ~]# cut -c 字符范围 <==用于排列整齐的信息参数:-d :后面接分隔字符。与 -f 一起使用;-f :依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思;-c :以字符 (characters) 的单位取出固定字符区间。


root@iZ25nbozf5vZ:~# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin...      
#以 : 分隔 PATH 再取出第2个字符串root@iZ25nbozf5vZ:~# echo $PATH | cut -d ':' -f 2/usr/local/bin
#以 : 分隔 PATH 再取出第2个和第4个字符串root@iZ25nbozf5vZ:~# echo $PATH | cut -d ':' -f 2,4/usr/local/bin:/usr/bin
#取出 PATH 的第5至第10个字符串root@iZ25nbozf5vZ:~# echo $PATH | cut -c 5-10/local
#取出 PATH 的第5个之后所有字符串root@iZ25nbozf5vZ:~# echo $PATH | cut -c 5-/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin...

cut 主要的用途在于将同一行里面的数据进行分解,最常使用在分析一些数据或文字数据的时候,这是因为有时候我们会以某些字符当作切割的参数,然后来将数据加以切割,以取得我们所需要的数据。


  • grep:刚才的 cut 是在一行信息当中取出某部分我们想要的,而 grep 则是分析一行信息,若当中有我们所需要的信息,就将该行拿出来,简单的语法是这样的:

[root@www ~]# grep [-acinv] [--color=auto] '查找字符串' filename参数:-a :将 binary 文件以 text 文件的方式查找数据;-c :计算找到 '查找字符串' 的次数;-i :忽略大小写的不同,所以大小写视为相同;-n :顺便输出行号;-v :反向选择,即显示出没有 '查找字符串' 内容的那一行!--color=auto :可以将找到的关键字部分加上颜色显示。


#将 last 当中有出现 root 的那一行就取出来。root@iZ25nbozf5vZ:~# last | grep 'root'root pts/0 113.87.181.65 Sun Mar 27 18:17 still logged in root pts/0 113.87.181.65 Sat Mar 26 16:04 - 18:08 (02:04)
#与上例相反,只要没有 root 的那一行就取出来root@iZ25nbozf5vZ:~# last | grep -v 'root'
wtmp begins Thu Mar 3 12:03:49 2022
#在 last 的输出信息中,只要有 root 就取出,并且仅取第一列root@iZ25nbozf5vZ:~# last | grep 'root' | cut -d ' ' -f 1rootroot
#取出 /etc/manpath.config 内含 MANPATH 的 那几行root@iZ25nbozf5vZ:~# grep 'MANPATH' /etc/manpath.config MANDATORY_MANPATH      /usr/manMANDATORY_MANPATH /usr/share/manMANDATORY_MANPATH      /usr/local/share/man...


2、排序命令:sort, wc, uniq

  • sort:

sort 可以帮我们进行排序,而且可以依据不同的数据类型来排序。例如数字与文字的排序就不一样。此外排序与语系的编码有关,因此如果你需要排序时,建议使用 LAND=C 来让语系统一,数据排序比较好一些。
[root@www ~]# sort [-fbMnrtuk] [file or stdin]参数:-f :忽略大小写的差异,例如 A 与 a 视为编码相同;-b :忽略最前面的空格符部分;-M :以月份的名字来排序,例如 JAN, DEC 等的排序方法;-n :使用“纯数字”进行排序(默认是以文字类型来排序的);-r :反向排序;-u :就是 uniq ,相同的数据中,仅出现一行代表;-t :分隔符,默认是用 [Tab] 键来分隔;-k :以那个区间 (field) 来进行排序的意思。

范例一:对个人账号进行排序,默认按账号名称首字母排序root@iZ25nbozf5vZ:~# cat /etc/passwd | sort backup:x:34:34:backup:/var/backups:/bin/shbin:x:2:2:bin:/bin:/bin/shdaemon:x:1:1:daemon:/usr/sbin:/bin/sh...
范例二:个人账号内容是以 : 分隔的,想以第三列来排序root@iZ25nbozf5vZ:~# cat /etc/passwd | sort -t ":" -k 3root:x:0:0:root:/root:/bin/bashwww:x:1000:1000::/alidata/www:/sbin/nologinlibuuid:x:100:101::/var/lib/libuuid:/bin/sh...
范例三:以第三列纯数字排序root@iZ25nbozf5vZ:~# cat /etc/passwd | sort -t ":" -k 3 -nroot:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/bin/shbin:x:2:2:bin:/bin:/bin/sh...


  • uniq:

uniq 命令可以帮助我们将重复的数据仅列出一个。
[root@www ~]# uniq [-ic]参数:-i :忽略大小写字符的不同;-c :进行计数。


范例一:使用 last 将账号列出,仅取出账号列,进行排序后仅取出一位。root@iZ25nbozf5vZ:~# last | cut -d ' ' -f 1 | sort | uniq
rootwtmp
范例二:承上例,如果我还想要知道每个人的登录总次数呢?root@iZ25nbozf5vZ:~# last | cut -d ' ' -f 1 | sort | uniq -c 1 1 root      1 wtmp# 从上面的结果可以发现 root 登录 1 次。# wtmp 与第一行的空白都是 last 的默认字符,那两个可以忽略。


  • wc:

如果我们想要知道 /etc/manpath.config 这个文件里面有多少字?多少行?多少字符的话,可以利用 wc 这个命令来完成。它可以帮我们计算输出的信息的整体数据。
[root@www ~]# wc [-lwm]参数:-l :仅列出行;-w :仅列出多少字(英文单字);-m :多少字符;


范例一:那个 /etc/manpath.config 里面到底有多少相关字、行、字符数?root@iZ25nbozf5vZ:~# cat /etc/manpath.config | wc  131 715 5173# 输出的三个数字中分别代表行、字数、字符数


推荐阅读