Efficient Linux at the Command Line by Daniel J. Barrett (O'Reilly). Copyright 2022 Daniel Barret, 978-1-098-11340-7.

输入(Input)、输出(Output)和管道(Pipes)

如果对一个很大的目录使用ls -l命令:

1
ls -l /bin
1
2
3
4
5
6
7
-rwxr-xr-x 1 root root       55744  4月  5  2024 [
-rwxr-xr-x 1 root root 14640 3月 31 2024 411toppm
-rwxr-xr-x 1 root root 38 4月 11 2024 7z
-rwxr-xr-x 1 root root 39 4月 11 2024 7za
-rwxr-xr-x 1 root root 39 4月 11 2024 7zr

lrwxrwxrwx 1 root root 4 8月 9 10:33 zstdmt -> zstd

该文件夹包含了太多文件。很遗憾ls不能一次性展示一页内容,在按下某个按键继续时暂停。但有另一个命令less,可以实现该功能。less命令可以使一个文件展示至全屏幕:

1
less myfile

我们可以将这两个命令结合,因为ls写入stdout而less从stdin读取。使用管道可以使ls的输出成为less的输入:

1
ls -l /bin | less

以上的命令可以使文件的文件一次只展示一屏幕。"|"被称作Linux管道符。任何包含管道的命令行被称作pipeline

六个命令

Efficient Linux at the Command Line中给了我们wc, head, cut, grep, sort, uniq六个命令,我们用这六个命令学习管道技巧。

下面是示例文件animals.txt

1
2
3
4
5
6
7
python  Programming Python      2010    Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal
robin MySQL High Availability 2014 Bell, Charles
horse Linux in a Nutshell 2005 Sielver, Ellen
donkey Cisco IOS in a Nutshell 2005 Bodney, James
oryx Writting Word Macros 1999 Roman, Steven

命令 #1: wc

wc命令打印文件的行数、词数和字符数:

1
wc animals.txt
1
7  51 328 animals.txt

wc告诉我们,animals.txt有7行,51个词和325个字符。

选项-l-w-c使wc命令只打印文件的行数、词数和字符数:

1
wc -l animals.txt
1
7 animals.txt

……

wc命令从stdin中读取,输出至stdout

我们用ls列举当前文件夹中的文件,然后管道至wc用来读取行数。那么这个管道命令就告诉我们“当前文件夹中有多少可见文件?”

1
ls -1
1
2
3
4
5
animals.txt
bsh
dir
main.py
Notes.md
1
ls -1 | wc -l
1
5

选项-1告诉ls使结果打印至1列。事实上同一个命令可以在管道命令中出现不止一次:

1
wc animals.txt | wc -w
1
4

与其他命令不同,ls能知道他是否在向stdout输出。

如果是向stdout输出,那么打印就会是多行的,如果被管道至其他命令,那么他就会输出至同一列。

我们可以使用-1-C选项使强至输出为一列或多列。

命令 #2: head

head命令打印文件的第一行。使用-n命令可以打印文件的前3行:

1
head -n3 animals.txt
1
2
3
python	Programming Python	2010	Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal

即使是对非常大的文件head命令也十分高效。

animals.txt的前三行计数:

1
head -n3 animals.txt | wc -w
1
20

head可以从stdin中读取,比如你只想看看一个命令输出的头几行:

1
ls /bin | head -n5
1
2
3
4
5
[
411toppm
7z
7za
7zr

命令 #3: cut

cut命令打印文件的一列或多列,比如,要打印animals.txt中的全部书名:

1
cut -f2 animals.txt
1
2
3
4
5
6
7
Programming Python
SSH, The Secure Shell
Intermediate Perl
MySQL High Availability
Linux in a Nutshell
Cisco IOS in a Nutshell
Writting Word Macros

cut提供了两种决定打印哪一列的方法。

一种是当字符串用制表符分隔时,可以使用-f。由于animals.txt有确切的格式,使用-f2选项就可以打印每一行的第2列。

也可以使用逗号分隔,使打印多列:

1
cut -f1,3 animals.txt | head -n3
1
2
3
python	2010
snail 2005
alpaca 2012

或者使用数字范围:

1
cut -f2-4 animals.txt | head -n3
1
2
3
Programming Python	2010	Lutz, Mark
SSH, The Secure Shell 2005 Barrett, Daniel
Intermediate Perl 2012 Schwartz, Randal

另一种方法是通过-c选项,决定输出文件的具体字符。同样,可使用逗号分隔或数字范围。

1
cut -c1-3 animals.txt
1
2
3
4
5
6
7
pyt
sna
alp
rob
hor
don
ory

假设现在我们需要所有作者的姓。可以使用-d选项(delimiter, 分隔符),使分隔符从制表符改为逗号:

1
cut -f4 animals.txt | cut -d, -f1
1
2
3
4
5
6
7
Lutz
Barrett
Schwartz
Bell
Sielver
Bodney
Roman

命令 #4: grep

grep命令极其强大,这里不作过多介绍。

下面的命令展示了animals.txt中含有字符串Nutshell的行:

1
grep Nutshell animals.txt
1
2
horse	Linux in a Nutshell	2005	Sielver, Ellen
donkey Cisco IOS in a Nutshell 2005 Bodney, James

使用-v命令可以查看animals.txt中不含有字符串Nutshell的行:

1
grep -v Nutshell animals.txt
1
2
3
4
5
python	Programming Python	2010	Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal
robin MySQL High Availability 2014 Bell, Charles
oryx Writting Word Macros 1999 Roman, Steven

总之,grep命令在查找文本中十分有用。下面的命令展示了含有Perl的*.txt文件:

1
gerp Perl *.txt
1
2
3
animals.txt:alpaca	Intermediate Perl	2012	Schwartz, Randal
p.txt:Perl programming language.
p.txt:languages such as Perl, Python, PHP and Ruby

grep从stdin中读取,并输出至stdout,因此可以很容易地使用管道。比如我们想知道文件夹/usr/lib中有多少子文件夹:

1
ls -l /usr/lib | cut -c1 |grep d | wc -l
1
108

我们注意到所有的文件夹都以d开头,用cut分离第一列(可能是d也可能不是),然后使用grep仅保留含d的行,最终使用wc统计行数。

命令 #5: sort

sort命令使文件的行按升序(默认)重新排列,例如:

1
sort animals.txt
1
2
3
4
5
6
7
alpaca	Intermediate Perl	2012	Schwartz, Randal
donkey Cisco IOS in a Nutshell 2005 Bodney, James
horse Linux in a Nutshell 2005 Sielver, Ellen
oryx Writting Word Macros 1999 Roman, Steven
python Programming Python 2010 Lutz, Mark
robin MySQL High Availability 2014 Bell, Charles
snail SSH, The Secure Shell 2005 Barrett, Daniel

或以降序排列(使用-r选项):

1
sort -r animals.txt
1
2
3
4
5
6
7
snail	SSH, The Secure Shell	2005	Barrett, Daniel
robin MySQL High Availability 2014 Bell, Charles
python Programming Python 2010 Lutz, Mark
oryx Writting Word Macros 1999 Roman, Steven
horse Linux in a Nutshell 2005 Sielver, Ellen
donkey Cisco IOS in a Nutshell 2005 Bodney, James
alpaca Intermediate Perl 2012 Schwartz, Randal

sort可以按字母表排列行,也可以按数字排列行(使用-n选项),例如:

1
cut -f3 animals.txt | sort -n
1
2
3
4
5
6
7
1999
2005
2005
2005
2010
2012
2014

如果我们要获得animals.txt中最新出版的书的年份,可以:

1
cut -f3 animals.txt | sort -nr | head -n1
1
2014

命令 #6: uniq

uniq命令检测相邻重复的行。默认将其移除。

假设文件letters含如下内容:

1
2
3
4
5
6
7
8
9
10
A
A
A
B
B
A
C
C
C
C
1
uniq letters
1
2
3
4
A
B
A
C

注意,uniq将开头的三个A更改为一个,但保留了最后一个A,以为其与前三个不相邻

使用-c选项可以展示出现频次:

1
uniq -c letters
1
2
3
4
3 A
2 B
1 A
4 C

检测重复文件

假设我们要检测如下JPEG文件是否重复;

1
2
3
4
image001.jpg  image005.jpg  image009.jpg  image013.jpg  image017.jpg
image002.jpg image006.jpg image010.jpg image014.jpg image018.jpg
image003.jpg image007.jpg image011.jpg image015.jpg image019.jpg
image004.jpg image008.jpg image012.jpg image016.jpg image020.jpg

我们需要另一个命令:md5sum,检测文件的内容并给出一个32位的字符串:检验和 (checksum):

1
md5sum image001.jpg
1
146b163929b6533f02e91bdf21cb9563  image001.jpg

一个文件的检验和非常非常可能是独特的。因此,如果两个文件的检验和是相同的,那么这两个文件很有可能是重复的。使用cut分离32位字符,然后用sort排序,使每个相同重复文件相邻。

1
md5sum *.jpg | cut -c1-32 | sort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1258012d57050ef6005739d0e6f6a257
146b163929b6533f02e91bdf21cb9563
146b163929b6533f02e91bdf21cb9563
17f339ed03733f402f74cf386209aeb3
1aa30608eb268a45266403f177f214d0
381ebc2cd3aab91a65492ef360714e2c
3825f1cffa61aee4673f5b7c535b2a09
63da88b3ddde0843c94269638dfa6958
714eceeb06b43c03fe20eb96474f69b8
8f8d01a6598833fb04abc69fe9e9572c
b965d5f3463e41eb66ea87a2933c407c
bc64c99757e199ef858f52acf7e4e836
bef69f30a2f88a20e81797ea65c1e082
c7978522c58425f6af3f095ef1de1cd5
c7978522c58425f6af3f095ef1de1cd5
c96a1094226ad766fc9367f508dc9b32
d8ad913044a51408ec1ed8a204ea9502
f6464ed766daca87ba407aede21c8fcc
f6464ed766daca87ba407aede21c8fcc
f6464ed766daca87ba407aede21c8fcc

这样就可以使用uniq函数来统计重复数,使用sort将出现重复的数至于顶部:

1
md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
1 d8ad913044a51408ec1ed8a204ea9502
1 c96a1094226ad766fc9367f508dc9b32
1 bef69f30a2f88a20e81797ea65c1e082
1 bc64c99757e199ef858f52acf7e4e836
1 b965d5f3463e41eb66ea87a2933c407c
1 8f8d01a6598833fb04abc69fe9e9572c
1 714eceeb06b43c03fe20eb96474f69b8
1 63da88b3ddde0843c94269638dfa6958
1 3825f1cffa61aee4673f5b7c535b2a09
1 381ebc2cd3aab91a65492ef360714e2c
1 1aa30608eb268a45266403f177f214d0
1 17f339ed03733f402f74cf386209aeb3
1 1258012d57050ef6005739d0e6f6a257

可以再使用grep命令去除未重复的文件:

1
2
3
4
md5sum *.jpg | cut -c1-32 | sort | uniq -c |sort -nr | grep -v "      1"
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
1
2
3
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563