Linux 中的 Cat 命令:基本和高级示例
cat 是 Linux 中最常用的命令之一,但你真的了解 cat 命令吗?了解 Linux 中 cat 命令的基础知识和高级示例。
继续上周从 ls 命令开始的那些不太为人所知的命令的浏览,今天让我们检查一下 cat
命令。
cat
名称代表 catenate
,因为该命令的主要工作是通过在标准输出上按顺序发送其内容来连接多个输入文件:
# Let's obtain first some sample data files:
curl -so - dict://dict.org/'d:felidae:gcide' | unexpand -a -t 3 |
sed -Ee '/^151/,/^[.]/!d;/^[.0-9]/s/.*//' > felidae.txt
curl -so - dict://dict.org/'d:felis:gcide' | unexpand -a -t 3 |
sed -Ee '/^151/,/^[.]/!d;/^[.0-9]/s/.*//' > felis.txt
# Catenate files
cat felidae.txt felis.txt
如果要将连接的结果存储在文件中,则必须使用 shell 重定向:
cat felidae.txt felis.txt > result.txt
cat result.txt
即使其主要设计目标是连接文件,cat
实用程序也经常仅使用一个参数来将该文件的内容显示到屏幕上,就像我在最后一行中所做的那样上面的例子。
A. 使用带有标准输入的 cat 命令
当不带任何参数使用时,cat
命令将从其标准输入读取数据并将其写入其标准输出 - 这基本上是无用的......除非您使用某些选项来转换数据。稍后我们将讨论一些有趣的选项。
除了文件路径之外,cat
命令还将 -
特殊文件名理解为标准输入的别名。这样,您可以将从标准输入读取的数据插入到命令行上给出的文件之间:
# Insert a separator between the two concatenated files
echo '----' | cat felis.txt - felidae.txt
B. 对二进制文件使用 cat 命令
1.加入分割文件
cat
命令不会对文件内容做出任何假设,因此它可以轻松地处理二进制数据。对于重新加入被 split
或 csplit
命令损坏的文件可能有用。或者像我们现在要做的那样加入部分下载:
#
# A picture by Von.grzanka (CC-SA 3.0)
# Optimize bandwidth usage by breaking the download in two parts
# (on my system, I observe a 10% gain that way compared to a "full" download)
curl -s -r 0-50000 \
https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Felis_catus-cat_on_snow.jpg/1024px-Felis_catus-cat_on_snow.jpg \
-o first-half &
curl -s -r 50001- \
https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Felis_catus-cat_on_snow.jpg/1024px-Felis_catus-cat_on_snow.jpg \
-o second-half &
wait
现在我们有了图像的两半。您可以使用 ImageMagick 的 display
或 gimp
或任何其他能够读取图像文件的软件打开前半部分,发现它已“损坏”:
display first-half
# -or-
gimp first-half
# -or-
firefox first-half
如果您研究我使用的 curl
命令,您会发现这两个部分是完美互补的。前半部分是从字节0到50000,后半部分是从字节50001到文件末尾。他们之间不应该有任何数据缺失。因此,我们只需将两个部分连接在一起(以正确的顺序)即可获取完整文件:
cat first-half second-half > image.jpg
display image.jpg
2. 使用流式文件格式
您不仅可以使用cat
命令“重新加入”被分割成多个部分的二进制文件,而且在某些情况下,您还可以创建新 这样的文件。这对于“无头”或“可流式”文件格式(例如 MPEG 传输流视频文件(.TS
文件))特别有效:
# Let's make a still video file from our picture
ffmpeg -y -loop 1 -i cat.jpg -t 3 \
-c:v libx264 -vf scale=w=800:h=-1 \
still.ts
# Let's make a fade-in from the same picture
ffmpeg -y -loop 1 -i cat.jpg -t 3 \
-c:v libx264 -vf scale=w=800:h=-1,fade=in:0:75 \
fadein.ts
# Let's make a fade-out from the same picture
ffmpeg -y -loop 1 -i cat.jpg -t 3 \
-c:v libx264 -vf scale=w=800:h=-1,fade=out:0:75 \
fadeout.ts
现在,我们可以使用 cat 命令组合所有这些传输流视频文件,在输出中获得完全有效的 TS 文件:
cat fadein.ts still.ts fadeout.ts > video.ts
mplayer video.ts
借助 TS 文件格式,您可以按照所需的顺序组合这些文件,甚至可以在参数列表中多次使用同一文件以在输出视频中创建循环或重复。显然,如果我们使用动画图像,这会更有趣,但我会让你自己做吗:许多消费级设备都会录制 TS 文件,如果不录制,你仍然可以使用 ffmpeg 将几乎所有视频文件转换为传输流文件。不要犹豫,使用评论部分分享您的作品!
3. 破解cpio档案
作为最后一个示例,让我们看看如何使用 cat
命令来合并多个 cpio
存档。但这一次,事情不会那么简单,因为它需要一些关于 cpio 存档文件格式的知识。
cpio 存档按顺序存储文件的元数据和内容,这使其适合与 cat 实用程序进行文件级串联。不幸的是,cpio 存档还包含一个用于标记存档结尾的预告片:
# Create two genuine CPIO `bin` archive:
$ find felis.txt felidae.txt | cpio -o > part1.cpio
2 blocks
$ echo cat.jpg | cpio -o > part2.cpio
238 blocks
$ hexdump -C part1.cpio | tail -7
000002d0 2e 0d 0a 09 09 20 20 5b 57 6f 72 64 4e 65 74 20 |..... [WordNet |
000002e0 31 2e 35 5d 0d 0a 0a 00 c7 71 00 00 00 00 00 00 |1.5].....q......|
000002f0 00 00 00 00 01 00 00 00 00 00 00 00 0b 00 00 00 |................|
00000300 00 00 54 52 41 49 4c 45 52 21 21 21 00 00 00 00 |..TRAILER!!!....|
00000310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000400
$ hexdump -C part2.cpio | tail -7
0001da40 46 96 ab f8 ad 11 23 90 32 79 ac 1f 8f ff d9 00 |F.....#.2y......|
0001da50 c7 71 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |.q..............|
0001da60 00 00 00 00 0b 00 00 00 00 00 54 52 41 49 4c 45 |..........TRAILE|
0001da70 52 21 21 21 00 00 00 00 00 00 00 00 00 00 00 00 |R!!!............|
0001da80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0001dc00
好消息是,对于 cpio 二进制存档,预告片的固定长度为 280 字节。因此,使用 head
标准命令,我们有一个简单的方法来删除它:
# Each archive end with the 280-byte trailer.
# To catenate both archives, just remove the trailer
# at the end of the first part:
$ head -c-280 part1.cpio | cat - part2.cpio > cat.cpio
$ cpio -it < cat.cpio
felis.txt
felidae.txt
cat.jpg
239 blocks
C. 基本的 cat 命令选项
在使用了各种二进制文件格式之后,现在让我们回到纯文本文件,研究几个专门用于处理这些文件的选项。虽然这些选项不是 POSIX 标准的一部分,但它们可以跨 BSD 和 GNU cat
实现移植。请注意,我在这里并不假装详尽无遗,因此请检查 man
以查看您系统上 cat
支持的选项的完整列表!
-n
:数字行
使用 n
选项,cat
命令将为每个输出行添加行号前缀:
cat -n felidae.txt
1
2 Felidae \Felidae\ n.
3 a natural family of lithe-bodied round-headed fissiped
4 mammals, including the cats; wildcats; lions; leopards;
5 cheetahs; and saber-toothed tigers.
6
7 Syn: family {Felidae}.
8 [WordNet 1.5]
9
-n
选项对输出行进行编号。这意味着从一个输入文件切换到下一个输入文件时,计数器不会重置,如果您自己尝试以下命令,您将看到它:
cat -n feli*.txt
-s
:抑制重复的空输出行
使用 -s
选项,cat
命令将把多个连续的空行折叠成一个:
cat -n felis.txt felidae.txt | sed -n 8,13p
8 lynx ({Felis lynx}) is also called {Lynx lynx}.
9 [1913 Webster +PJC]
10
11
12 Felidae \Felidae\ n.
13 a natural family of lithe-bodied round-headed fissiped
sylvain@bulbizarre:~$ cat -ns felis.txt felidae.txt | sed -n 8,13p
8 lynx ({Felis lynx}) is also called {Lynx lynx}.
9 [1913 Webster +PJC]
10
11 Felidae \Felidae\ n.
12 a natural family of lithe-bodied round-headed fissiped
13 mammals, including the cats; wildcats; lions; leopards;
在上面的示例中,您可以看到,在默认输出中,第 10 行和第 11 行为空。添加 -s
选项时,第二个空行被丢弃。
-b
:仅对非空行进行编号
与前面的两个选项有些相关,-b
选项将对行进行编号,但忽略空行:
$ cat -b felidae.txt | cat -n
1
2 1 Felidae \Felidae\ n.
3 2 a natural family of lithe-bodied round-headed fissiped
4 3 mammals, including the cats; wildcats; lions; leopards;
5 4 cheetahs; and saber-toothed tigers.
6 5
7 6 Syn: family {Felidae}.
8 7 [WordNet 1.5]
9
上面的示例使用了 cat
命令的两个实例,管道中具有不同的选项。内部编号来自与第一个 cat
命令一起使用的 -b
选项。外部编号来自与第二个 cat
一起使用的 -n
选项。
正如您所看到的,第一行和最后一行没有由-b
选项编号,因为它们是空的。但是第 6 行呢?为什么仍然使用 -b
选项进行编号?嗯,因为它是一个空白行,但不是一个空行,我们将在下一篇中看到部分。
-v
、-e
、-t
:显示非打印字符
三个选项 -v
、-e ` 和 `-t
用于显示不同的不可见字符集。即使这些集合重叠,也没有“包罗万象”的选项,因此如果您想显示所有不可见字符,则必须将它们组合起来。
-v
:查看不可见字符
-v
选项显示所有带有插入符号和元表示法的非打印字符,除了换行符和制表符。
使用该选项,控制字符将显示为脱字号 (^
),后跟相应的 ASCII 字符(例如,回车符、字节 13 显示为 ^M
,因为ASCII 中的 M
为 64 + 13),设置高位的字符将以“meta”表示法 M-
后跟对应于低 7 位的表示形式位(例如,字节 141 将显示为 M-^M
因为 141 是 128 + 13)。
虽然看似深奥,但该功能在处理二进制文件时可能很有用,例如,如果您想检查嵌入在 JPEG 文件中的原始信息:
$ cat -v cat.jpg | fold -75 | head -10
M-^?M-XM-^?M-`^@^PJFIF^@^A^A^A^@H^@H^@^@M-^?M-~^@QFile source: http://commo
ns.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpgM-^?M-b^LXICC_PROFILE
^@^A^A^@^@^LHLino^B^P^@^@mntrRGB XYZ ^GM-N^@^B^@ ^@^F^@1^@^@acspMSFT
^@^@^@^@IEC sRGB^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-vM-V^@^A^@^@^@^@M-S-HP ^@^@^
@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^Qcprt^@^@^AP^@^@^@3desc^@^@^AM-^D^@^@^@lwtpt^@^@^AM-p^@^@^@^
Tbkpt^@^@^B^D^@^@^@^TrXYZ^@^@^B^X^@^@^@^TgXYZ^@^@^B,^@^@^@^TbXYZ^@^@^B@^@^@
^@^Tdmnd^@^@^BT^@^@^@pdmdd^@^@^BM-D^@^@^@M-^Hvued^@^@^CL^@^@^@M-^Fview^@^@^
CM-T^@^@^@$lumi^@^@^CM-x^@^@^@^Tmeas^@^@^D^L^@^@^@$tech^@^@^D0^@^@^@^LrTRC^
@^@^D<^@^@^H^LgTRC^@^@^D<^@^@^H^LbTRC^@^@^D<^@^@^H^Ltext^@^@^@^@Copyright (
-v
选项的另一个用例是查找可能泄漏到文本文件中的控制字符。如果您还记得的话,我们在上面遇到了一个奇怪的问题,即 -b
选项对第 6 个输入行进行编号,而它看起来就像是空的。那么让我们调查一下:
$ cat -v felidae.txt
Felidae \Felidae\ n.^M
a natural family of lithe-bodied round-headed fissiped^M
mammals, including the cats; wildcats; lions; leopards;^M
cheetahs; and saber-toothed tigers.^M
^M
Syn: family {Felidae}.^M
[WordNet 1.5]^M
啊啊!你看到那些^M
标记了吗?它们用于替换其他不可见的回车符。它从哪里来的?与任何其他 Internet 协议一样,dict
协议使用 CRLF 作为行终止符。因此我们将它们作为示例文件的一部分下载。您可以在 fold
和 fmt
文章中了解有关换行和回车的更多信息。但现在,它解释了为什么 cat
认为第 6 行不为空。
-e
:显示不可见字符,包括行尾
-e
选项的工作方式类似于 -v
选项,不同之处在于它还会在每个换行符之前添加一个美元符号 ($
),因此明确显示行尾:
$ cat -e felidae.txt
$
Felidae \Felidae\ n.^M$
a natural family of lithe-bodied round-headed fissiped^M$
mammals, including the cats; wildcats; lions; leopards;^M$
cheetahs; and saber-toothed tigers.^M$
^M$
Syn: family {Felidae}.^M$
[WordNet 1.5]^M$
$
-t
:显示不可见字符,包括制表符
-t
选项的工作方式类似于 -v
选项,不同之处在于它还会使用 ^I
脱字符号显示制表符(制表符存储为保存值 9 的字节,ASCII 中的 I
为 64+9=73):
$ cat -t felidae.txt
Felidae \Felidae\ n.^M
^Ia natural family of lithe-bodied round-headed fissiped^M
^Imammals, including the cats; wildcats; lions; leopards;^M
^Icheetahs; and saber-toothed tigers.^M
^M
^ISyn: family {Felidae}.^M
^I^I [WordNet 1.5]^M
-et
:显示所有隐藏字符
正如我已经简要提到的,如果您想显示所有非打印字符,包括制表符和行尾标记,则需要同时使用 -e
和 -选项:
$ cat -et felidae.txt
$
Felidae \Felidae\ n.^M$
^Ia natural family of lithe-bodied round-headed fissiped^M$
^Imammals, including the cats; wildcats; lions; leopards;^M$
^Icheetahs; and saber-toothed tigers.^M$
^M$
^ISyn: family {Felidae}.^M$
^I^I [WordNet 1.5]^M$
$
奖励:Linux 中 cat 命令的无用使用
如果没有提到“无用地使用 Cat”反模式,任何有关 cat
命令的文章都是不完整的。
当您使用 cat
的唯一目的是将文件内容发送到另一个命令的标准输入时,就会发生这种情况。使用 cat
命令被认为是“无用的”,因为简单的重定向或文件名参数就可以完成这项工作,而且会做得更好。但一个值得一千字的例子:
$ curl -so - dict://dict.org/'d:uuoc:jargon' | sed -Ee '/^151/,/^[.]/!d;/^[.0-9]/s/.*//' > uuoc.txt
$ cat uuoc.txt | less
UUOC
[from the comp.unix.shell group on Usenet] Stands for Useless Use of {cat};
the reference is to the Unix command cat(1), not the feline animal. As
received wisdom on comp.unix.shell observes, ?The purpose of cat is to
concatenate (or ?catenate?) files. If it's only one file, concatenating it
with nothing at all is a waste of time, and costs you a process.?
Nevertheless one sees people doing
cat file | some_command and its args ...
instead of the equivalent and cheaper
<file some_command and its args ...
or (equivalently and more classically)
some_command and its args ... <file
[...]
在上面的示例中,我使用管道通过 less
分页器显示 uuoc.txt
文件的内容:
cat uuoc.txt | less
因此,cat
命令的唯一目的是向 less 命令的标准输入提供 uuoc.txt
文件的内容。我会使用 shell 重定向获得相同的行为:
less < uuoc.txt
事实上,less
命令与许多命令一样,也接受文件名作为参数。所以我可以简单地写成:
less uuoc.txt
正如您所看到的,这里不需要 cat
。如果我提到“猫的无用使用”反模式,这是因为,如果您在论坛或其他地方公开使用它,毫无疑问有人会指出您将创建一个“无用的额外过程”。 ”
我必须承认,很长一段时间我对这样的评论很不屑一顾。毕竟,在我们的现代硬件上,为一次性操作生成一个额外的进程不会导致那么多开销。
但在撰写本文时,我做了一个快速实验,通过测试 awk
脚本比较了使用和不使用 UUOC 时处理来自慢速介质的 500MB 数据所需的时间。
令我惊讶的是,差异绝非可以忽略不计:
然而,原因并不是创建了额外的进程。但由于 UUOC 需要额外的读/写和上下文切换(您可以从执行系统代码所花费的时间推断出这一点)。因此,事实上,当您处理大型数据集时,额外的 cat
命令会产生不可忽略的成本。至于我自己,我现在会尽量提高警惕!你呢?如果您有 Cat 无用用途的例子,请随时与我们分享!