shell管道命令(pipe)

shell管道命令(pipe)

功能:
管道命令操作符是:| , 它仅能处理前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandard error 信息没有直接处理能力。然后将输出信息传递给下一个命令,作为标准输入 standard input。

用法:
在每个管线后面接的第一个数据必定是『命令』,而且这个命令必须要能够接受 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:
功能:
从一行数据中取出想要的部分,选取信息通常以“行”来分析,并不是整篇信息分析的。

格式:
cut -d ‘分隔字符’ -f fields 用于分隔并截取字符串
cut -c 字符范围 用于选取字符串片段

举例:

echo $PATH | cut -d ':' -f 5  # 字符串以:分割,并选取第5个
export | cut -c 12-  #截取字符串索引12以后的部分

grep:
功能:
分析一行信息,若当中有感兴趣的信息,就将该行拿出来,可以用于正则表达式

格式:
grep [-acinv] [–color=auto] ‘查找字符串’ filename
参数说明:
-a :将 binary 文件以 text 文件的方式搜寻数据
-c :计算找到 ‘搜寻字符串’ 的次数
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 ‘搜寻字符串’ 内容的那一行!
–color=auto :可以将找到的关键词部分着色显示

举例:

last | grep 'root' | cut -d ' ' -f1  # 取出含root的行,并选取第一列
grep --color=auto 'MANPATH' /etc/man.config  # 将文件man.config中含MANPATH的部分着色显示

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

sort:
功能:
根据不同的数据类型进行排序,另外排序的字符与语系的编码有关,因此如果需要排序,建议使用LANG=C来让语系统一。

格式:
sort [-fbMnrtuk] [file or stdin]
参数说明:
-f :忽略大小写的差异,例如 A 与 a 视为编码相同;
-b :忽略最前面的空格符部分;
-M :以月份的名字来排序,例如 JAN, DEC 等等的排序方法;
-n :使用『纯数字』进行排序(默认是以文字型态来排序的);
-r :反向排序;
-u :就是 uniq ,相同的数据中,仅出现一行代表;
-t :分隔符,默认是用 [tab] 键来分隔;
-k :以那个区间 (field) 来进行排序的意思

举例:

>> sort /etc/passwd | sort  # 对个人账号排序,默认第一个数据按照字符类型排序
_apt:x:105:65534::/nonexistent:/bin/false
avahi-autoipd:x:113:124:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
avahi:x:114:125:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin

>> sort /etc/passwd | sort -t ':' -k 3  # 以空格分割,按照第三个字符排序
root:x:0:0:root:/root:/bin/bash
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false

>> sort /etc/passwd | sort -t ':' -k 3 -n  # 以空格分割,按照第三个字符以数字格式排序
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin

uniq:
功能:
这个命令用来将重复的行删除掉只显示一个,需要配合排序过的文件来处理。

格式:
uniq [-ic]
参数说明:
-i:忽略大小写
-c:进行计数

举例:

>> last | cut -d ' ' -f1 | sort  # 对登陆的用户排序
guest-9s
guest-9s
reboot
root
root
root
...

>> last | cut -d ' ' -f1 | sort | uniq  # 输出登录过得用户名
guest-9s
reboot
root
wtmp

>> last | cut -d ' ' -f1 | sort | uniq -c  # 对登录用户进行计数
 2 guest-9s
 1 reboot
41 root
 1 wtmp

wc:
功能:
常用于统计一个目录中包含多少文件(多少行,多少字,多少字符)

格式:
wc [-lwm]
参数说明:
-l:仅列出行
-w:仅列出字数
-m:多少字符

举例:

>> cat /etc/man.config | wc

>> last | grep [a-zA-Z] | grep -v 'wtmp' | wc -l  # 统计除空格和wtmp外其他用户登录的次数
44

3、双向重定向:tee

tee:
功能:
tee 会同时将数据流分送到文件去与屏幕 (screen);而输出到屏幕的,其实就是 stdout,可以让下个命令继续处理。
格式:
tee [-a] file
参数说明:
-a:以累加(append)的方式,将数据加入到file当中。

举例:

>> last | tee last.list | cut -d " " -f1
# 这个范例可以让我们将 last 的输出存一份到 last.list 文件中;

>> ls -l /home | tee ~/homefile | more
# 这个范例则是将 ls 的数据存一份到 ~/homefile ,同时屏幕也有输出信息!

>> ls -l / | tee -a ~/homefile | more
# 要注意! tee 后接的文件会被覆盖,若加上 -a 这个选项则能将信息累加。

4、字符转换命令:tr、col、join、paste、expand

tr:
功能:
tr 可以用来删除一段信息当中的文字,或者是进行文字信息的替换。
tr可以去除dos文件留下来的\^M这个断行符号,^M可以使用\r代替。

格式:
tr [-ds] SET1 …
选项与参数:
-d :删除信息当中的 SET1 这个字符串;
-s :取代掉重复的字符;

举例:

范例一:将 last 输出的信息中,所有的小写变成大写字符:
>> last | tr '[a-z]' '[A-Z]'
# 事实上,没有加上单引号也是可以运行的,如:『 last | tr [a-z] [A-Z] 』

范例二:将 /etc/passwd 输出的信息中,将冒号 (:) 删除
>> cat /etc/passwd | tr -d ':'

范例三:将 /etc/passwd 转存成 dos 断行到 /root/passwd 中,再将 ^M 符号删除
>> cp /etc/passwd /root/passwd && unix2dos /root/passwd
>> file /etc/passwd /root/passwd
/etc/passwd:  ASCII text
/root/passwd: ASCII text, with CRLF line terminators <==就是 DOS 断行

>> cat /root/passwd | tr -d '\r' > /root/passwd.linux
# 那个 \r 指的是 DOS 的断行字符,关于更多的字符,请参考 man tr
>> ll /etc/passwd /root/passwd*
-rw-r--r-- 1 root root 1986 Feb  6 17:55 /etc/passwd
-rw-r--r-- 1 root root 2030 Feb  7 15:55 /root/passwd
-rw-r--r-- 1 root root 1986 Feb  7 15:57 /root/passwd.linux
# 处理过后,发现文件大小与原本的 /etc/passwd 就一致了!

用途:
(1) 这个命令可以用在正则表达式里,因为它也是由正则表达式的方式来替换数据的。
(2) 以上面的例子来说,使用 []可以配置一串字,也常常用来取代文件中的怪异符号。
(3) tr可以去除dos文件留下来的\^M这个断行符号,^M可以使用\r代替,参见范例3。


col:
功能:
将 tab 键转换成对等的空格键

格式:
col [-xb]
选项与参数:
-x :将 tab 键转换成对等的空格键
-b :在文字内有反斜杠 (/) 时,仅保留反斜杠最后接的那个字符

举例:

范例一:利用 cat -A 显示出所有特殊按键,最后以 col 将 [tab] 转成空白
>> cat -A /etc/man.config  <==此时会看到很多 ^I 的符号,那就是 tab
>> cat /etc/man.config | col -x | cat -A | more
# 嘿嘿!如此一来, [tab] 按键会被取代成为空格键,输出就美观多了!

范例二:将 col 的 man page 转存成为 /root/col.man 的纯文本档
>> man col > /root/col.man
>> vi /root/col.man
COL(1)          BSD General Commands Manual               COL(1)

N^HNA^HAM^HME^HE
     c^Hco^Hol^Hl - filter reverse line feeds from input

S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS
     c^Hco^Hol^Hl [-^H-b^Hbf^Hfp^Hpx^Hx] [-^H-l^Hl _^Hn_^Hu_^Hm]
# 你没看错!由于 man page 内有些特殊按钮会用来作为类似特殊按键与颜色显示,
# 所以这个文件内就会出现如上所示的一堆怪异字符(有 ^ 的)

>> man col | col -b > /root/col.man

用途:
虽然 col 有他特殊的用途,不过,很多时候,他可以用来简单的处理将 [tab] 按键取代成为空格键! 例如上面的例子当中,如果使用 cat -A 则 [tab] 会以 ^I 来表示。 但经过 col -x 的处理,则会将 [tab] 取代成为对等的空格键!此外, col 经常被利用于将 man page 转存为纯文本文件以方便查阅的功能!如上述的范例二!


join:
功能:
join 看字面上的意义 (加入) 就可以知道,他是在处理两个文件之间的数据, 而且,主要是在处理『两个文件当中,有 “相同数据” 的那一行,才将他加在一起』的意思。

格式:
join [-ti12] file1 file2
选项与参数:
-t :join 默认以空格符分隔数据,并且比对『第一个字段』的数据,
如果两个文件相同,则将两笔数据联成一行,且第一个字段放在第一个!
-i :忽略大小写的差异;
-1 :这个是数字的 1 ,代表『第一个文件要用那个字段来分析』的意思;
-2 :代表『第二个文件要用那个字段来分析』的意思。

举例:

范例一:用 root 的身份,将 /etc/passwd 与 /etc/shadow 相关数据整合成一栏
>> head -n 3 /etc/passwd /etc/shadow
==> /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

==> /etc/shadow <==
root:$1$/3AQpE5e$y9A/D0bh6rElAs:14120:0:99999:7:::
bin:*:14126:0:99999:7:::
daemon:*:14126:0:99999:7:::
# 由输出的数据可以发现这两个文件的最左边字段都是账号!且以 : 分隔

>> join -t ':' /etc/passwd /etc/shadow
root:x:0:0:root:/root:/bin/bash:$1$/3AQpE5e$y9A/D0bh6rElAs:14120:0:99999:7:::
bin:x:1:1:bin:/bin:/sbin/nologin:*:14126:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:14126:0:99999:7:::
# 透过上面这个动作,我们可以将两个文件第一字段相同者整合成一行!
# 第二个文件的相同字段并不会显示(因为已经在第一行了嘛!)

范例二:我们知道 /etc/passwd 第四个字段是 GID ,那个 GID 记录在 /etc/group 当中的第三个字段,请问如何将两个文件整合?
>> head -n 3 /etc/passwd /etc/group
==> /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

==> /etc/group <==
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
# 从上面可以看到,确实有相同的部分喔!赶紧来整合一下!

>> join -t ':' -1 4 /etc/passwd -2 3 /etc/group
0:root:x:0:root:/root:/bin/bash:root:x:root
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:root,bin,daemon
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:root,bin,daemon
# 同样的,相同的字段部分被移动到最前面了!所以第二个文件的内容就没再显示。
# 请读者们配合上述显示两个文件的实际内容来比对!

用途:
(1) 将有相关性的数据联结在一起;
(2) 在使用join前,要处理的文件应该事先经过排序(sort)处理。


paste:
功能:
这个 paste 就要比 join 简单多了!相对于 join 必须要比对两个文件的数据相关性, paste 就直接『将两行贴在一起,且中间以 [tab] 键隔开』而已!

格式:
paste [-d] file1 file2
选项与参数:
-d :后面可以接分隔字符。默认是以 [tab] 来分隔的!
- :如果 file 部分写成 - ,表示来自 standard input 的数据的意思。

举例:

范例一:将 /etc/passwd 与 /etc/shadow 同一行贴在一起
[root@www ~]# paste /etc/passwd /etc/shadow
bin:x:1:1:bin:/bin:/sbin/nologin        bin:*:14126:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:14126:0:99999:7:::
adm:x:3:4:adm:/var/adm:/sbin/nologin    adm:*:14126:0:99999:7:::
# 注意喔!同一行中间是以 [tab] 按键隔开的!

范例二:先将 /etc/group 读出(用 cat),然后与范例一贴上一起!且仅取出前三行
[root@www ~]# cat /etc/group|paste /etc/passwd /etc/shadow -|head -n 3
# 这个例子的重点在那个 - 的使用!那玩意儿常常代表 stdin 喔!

expand:
功能:
就是在将 [tab] 按键转成空格键。

格式:
expand [-t] file
选项与参数:
-t :后面可以接数字。一般来说,一个 tab 按键可以用 8 个空格键取代。
我们也可以自行定义一个 [tab] 按键代表多少个字符呢!

举例:

范例一:将 /etc/man.config 内行首为 MANPATH 的字样就取出;仅取前三行;
>> grep '^MANPATH' /etc/man.config | head -n 3
MANPATH /usr/man
MANPATH /usr/share/man
MANPATH /usr/local/man
# 行首的代表标志为 ^ ,这个我们留待下节介绍!先有概念即可!

范例二:承上,如果我想要将所有的符号都列出来?(用 cat)
>> grep '^MANPATH' /etc/man.config | head -n 3 |cat -A
MANPATH^I/usr/man$
MANPATH^I/usr/share/man$
MANPATH^I/usr/local/man$
# 发现差别了吗?没错~ [tab] 按键可以被 cat -A 显示成为 ^I 

范例三:承上,我将 [tab] 按键配置成 6 个字符的话?
>> grep '^MANPATH' /etc/man.config | head -n 3 | expand -t 6 - | cat -A
MANPATH     /usr/man$
MANPATH     /usr/share/man$
MANPATH     /usr/local/man$
123456123456123456.....
# 仔细看一下上面的数字说明,因为我是以 6 个字符来代表一个 [tab] 的长度,所以,
# MAN... 到 /usr 之间会隔 12 (两个 [tab]) 个字符喔!如果 tab 改成 9 的话,
# 情况就又不同了!这里也不好理解~您可以多配置几个数字来查阅就晓得!

5、切割命令:split

功能:
如果文件太大,可以使用split将一个大文件依据文件大小或行数来切割成为小文件。

格式:
split [-bl] file PREFIX
选项与参数:
-b :后面可接欲分割成的文件大小,可加单位,例如 b, k, m 等;
-l :以行数来进行分割。
PREFIX :代表前导符的意思,可作为分割文件的前导文字。

举例:

范例一:我的 /etc/termcap 有七百多K,若想要分成 300K 一个文件时?
[root@www ~]# cd /tmp; split -b 300k /etc/termcap termcap
[root@www tmp]# ll -k termcap*
-rw-r--r-- 1 root root 300 Feb  7 16:39 termcapaa
-rw-r--r-- 1 root root 300 Feb  7 16:39 termcapab
-rw-r--r-- 1 root root 189 Feb  7 16:39 termcapac
# 那个档名可以随意取的啦!我们只要写上前导文字,小文件就会以
# xxxaa, xxxab, xxxac 等方式来创建小文件的!

范例二:如何将上面的三个小文件合成一个文件,档名为 termcapback
[root@www tmp]# cat termcap* >> termcapback
# 很简单吧?就用数据流重导向就好啦!简单!

范例三:使用 ls -al / 输出的信息中,每十行记录成一个文件
[root@www tmp]# ls -al / | split -l 10 - lsroot
[root@www tmp]# wc -l lsroot*
  10 lsrootaa
  10 lsrootab
   6 lsrootac
  26 total
# 重点在那个 - 啦!一般来说,如果需要 stdout/stdin 时,但偏偏又没有文件,
# 有的只是 - 时,那么那个 - 就会被当成 stdin 或 stdout ~

6、参数代换:xargs

功能:
xargs 是在做什么的呢?就以字面上的意义来看, x 是加减乘除的乘号,args 则是 arguments (参数) 的意思,所以说,这个玩意儿就是在产生某个命令的参数的意思! xargs 可以读入 stdin 的数据,并且以空格符或断行字符作为分辨,将 stdin 的数据分隔成为 arguments 。 因为是以空格符作为分隔,所以,如果有一些档名或者是其他意义的名词内含有空格符的时候, xargs 可能就会误判了。

格式:
xargs [-0epn] command
选项与参数:
-0 :如果输入的 stdin 含有特殊字符,例如 `, , 空格键等等字符时,这个 -0 参数可以将他还原成一般字符。这个参数可以用于特殊状态喔!
-e :这个是 EOF (end of file) 的意思。后面可以接一个字符串,当 xargs 分析到这个字符串时,就会停止继续工作!
-p :在运行每个命令的 argument 时,都会询问使用者的意思;
-n :后面接次数,每次 command命令运行时,要使用几个参数的意思。看范例三。
当 xargs 后面没有接任何的命令时,默认是以 echo 来进行输出喔!

举例:

范例一:将 /etc/passwd 内的第一栏取出,仅取三行,使用 finger 这个命令将每个账号内容秀出来
>> cut -d':' -f1 /etc/passwd |head -n 3| xargs finger
Login: root                             Name: root
Directory: /root                        Shell: /bin/bash
Never logged in.
No mail.
No Plan.
......底下省略.....
# 由 finger account 可以取得该账号的相关说明内容,例如上面的输出就是 finger root
# 后的结果。在这个例子当中,我们利用 cut 取出账号名称,用 head 取出三个账号,
# 最后则是由 xargs 将三个账号的名称变成 finger 后面需要的参数!

范例二:同上,但是每次运行 finger 时,都要询问使用者是否动作?
>> cut -d':' -f1 /etc/passwd |head -n 3| xargs -p finger
finger root bin daemon ?...y
.....(底下省略)....
# 呵呵!这个 -p 的选项可以让用户的使用过程中,被询问到每个命令是否运行!

范例三:将所有的 /etc/passwd 内的账号都以 finger 查阅,但一次仅查阅五个账号
>> cut -d':' -f1 /etc/passwd | xargs -p -n 5 finger
finger root bin daemon adm lp ?...y
.....(中间省略)....
finger uucp operator games gopher ftp ?...y
.....(底下省略)....
# 在这里鸟哥使用了 -p 这个参数来让您对于 -n 更有概念。一般来说,某些命令后面
# 可以接的 arguments 是有限制的,不能无限制的累加,此时,我们可以利用 -n
# 来帮助我们将参数分成数个部分,每个部分分别再以命令来运行!这样就 OK 啦!^_^

范例四:同上,但是当分析到 lp 就结束这串命令?
>> cut -d':' -f1 /etc/passwd | xargs -p -e'lp' finger
finger root bin daemon adm ?...
# 仔细与上面的案例做比较。也同时注意,那个 -e'lp' 是连在一起的,中间没有空格键。
# 上个例子当中,第五个参数是 lp 啊,那么我们下达 -e'lp' 后,则分析到 lp
# 这个字符串时,后面的其他 stdin 的内容就会被 xargs 舍弃掉了!

使用xargs的原因是,很多命令其实并不支持管道命令,因此我们可以通过xargs来提供该命令引用standard input之用:

范例五:找出 /sbin 底下具有特殊权限的档名,并使用 ls -l 列出详细属性
>> find /sbin -perm +7000 | ls -l
# 结果竟然仅有列出 root 所在目录下的文件!这不是我们要的!
# 因为 ll (ls) 并不是管线命令的原因啊!

>> find /sbin -perm +7000 | xargs ls -l
-rwsr-xr-x 1 root root 70420 May 25  2008 /sbin/mount.nfs
-rwsr-xr-x 1 root root 70424 May 25  2008 /sbin/mount.nfs4
-rwxr-sr-x 1 root root  5920 Jun 15  2008 /sbin/netreport
....(底下省略)....

7、关于减号-的用途

功能:
在管道命令中,经常会用到前一个命令的stdout作为这次的stdin,某些命令需要用到文件名来进行处理时,该stdin与stdout可以利用减号“-”来替代。

tar -cvf - /home | tar -xvf -

参考:
《鸟哥的Linux私房菜 基础学习篇 第三版》

猜你喜欢

转载自blog.csdn.net/olizxq/article/details/81263867