Linux 中 Paste 命令的 7 个基本实用用法
了解如何在实际示例中使用粘贴实用程序来合并文本文件,同时发现该命令的一些技巧和陷阱。
在上一篇文章中,我们讨论了 cut 命令,它可用于从 CSV 或表格文本数据文件中提取列。
paste
命令的作用恰恰相反:它合并多个输入文件以生成一个新的分隔文本文件。我们将了解如何在 Linux 和 Unix 中有效地使用粘贴命令。
Linux下的7个paste命令的实用例子
如果您喜欢视频,可以观看此视频,该视频解释了本文中讨论的相同粘贴命令示例。
1. 粘贴列
在最基本的用例中,paste
命令采用 N 个输入文件并将它们逐行连接到输出上。
在下面的示例中,我使用 bash printf 命令来格式化输入文件的输出。
sh$ printf "%s\n" {a..e} | tee letters
a
b
c
d
e
sh$ printf "%s\n" {1..5} | tee digits
1
2
3
4
5
sh$ paste letters digits
a 1
b 2
c 3
d 4
e 5
但现在让我们离开理论解释来讨论一个实际的例子。如果您已经下载了上面视频中使用的示例文件,您可以看到我有几个数据文件对应于表格的各个列:
sh$ head -3 *.csv
==> ACCOUNTLIB.csv <==
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC
==> ACCOUNTNUM.csv <==
ACCOUNTNUM
623477
445452
==> CREDIT.csv <==
CREDIT
<--- empty line
<--- empty line
==> DEBIT.csv <==
DEBIT
00000001615,00
00000000323,00
从这些数据生成制表符分隔的文本文件非常容易:
sh$ paste *.csv | head -3
ACCOUNTLIB ACCOUNTNUM CREDIT DEBIT
TIDE SCHEDULE 623477 00000001615,00
VAT BS/ENC 445452 00000000323,00
正如您所看到的,当显示在控制台上时,该制表符分隔值文件的内容不会生成格式完美的表格。但这是设计使然:paste
命令不用于创建固定宽度的文本文件,而仅用于创建分隔文本文件,其中给定字符被指定为字段分隔符的角色。
因此,即使在上面的输出中并不明显,每个字段之间实际上有一个且只有一个制表符。让我们通过使用 sed 命令来清楚地表明这一点:
sh$ paste *.csv | head -3 | sed -n l
ACCOUNTLIB\tACCOUNTNUM\tCREDIT\tDEBIT$
TIDE SCHEDULE\t623477\t\t00000001615,00$
VAT BS/ENC\t445452\t\t00000000323,00$
现在,不可见字符在输出中明确显示。您可以看到制表符显示为 \t
。您可以数一下它们:每个输出行上始终有三个选项卡 - 每个字段之间都有一个选项卡。当你看到其中两个连续时,这只意味着那里有一个空的字段。在我的特定示例文件中经常出现这种情况,因为在每一行上,都会设置 CREDIT 或 DEBIT 字段,但绝不会同时设置这两个字段。
2. 更改字段分隔符
正如我们所见,paste
命令使用制表符作为默认字段分隔符(“分隔符”)。我们可以使用 -d
选项进行更改。假设我想使用分号代替:
# The quotes around the ';' are used to prevent the
# shell to consider that semi-colon as being a command separator
sh$ paste -d ';' *.csv | head -3
ACCOUNTLIB;ACCOUNTNUM;CREDIT;DEBIT
TIDE SCHEDULE;623477;;00000001615,00
VAT BS/ENC;445452;;00000000323,00
无需在管道末尾附加 sed 命令,因为我们使用的分隔符是可打印字符。无论如何,结果是相同的:在给定行上,每个字段都使用单字符分隔符与其相邻字段分隔开。
3. 使用串行模式转置数据
上面的示例有一个共同点:paste 命令并行读取所有输入文件,这是它可以在输出中逐行合并它们所必需的。
但是,paste
命令还可以在所谓的串行模式下运行,使用 -s
标志启用。顾名思义,在串行模式下,paste
命令将依次读取输入文件。第一个输入文件的内容将用于生成第一个输出行。然后第二个输入文件的内容将用于生成第二个输出行,依此类推。这也意味着输出的行数与输入中的文件数一样多。
更正式地说,从文件 N 中获取的数据将在串行模式的输出中显示为第 N 行,而在默认“并行”模式下它将显示为第 N 列。用数学术语来说,串行模式下获得的表是默认模式下生成的表的转置(反之亦然)。
为了说明这一点,让我们考虑数据的一个小子样本:
sh$ head -5 ACCOUNTLIB.csv | tee ACCOUNTLIB.sample
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC
PAYABLES
ACCOMMODATION GUIDE
sh$ head -5 ACCOUNTNUM.csv | tee ACCOUNTNUM.sample
ACCOUNTNUM
623477
445452
4356
623372
在默认(“并行”)模式下,输入文件的数据将作为输出中的列,生成一个两列乘五行的表:
sh$ paste *.sample
ACCOUNTLIB ACCOUNTNUM
TIDE SCHEDULE 623477
VAT BS/ENC 445452
PAYABLES 4356
ACCOMMODATION GUIDE 623372
但在串行模式下,输入文件的数据将显示为行,现在生成一个五列乘两行的表:
sh$ paste -s *.sample
ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC PAYABLES ACCOMMODATION GUIDE
ACCOUNTNUM 623477 445452 4356 623372
4. 使用标准输入
与许多标准实用程序一样,paste
命令可以使用标准输入来读取数据。当没有文件名作为参数给出时隐式地显示,或者通过使用特殊的 -
文件名显式地显示。显然,这并没有那么有用:
# Here, the paste command is useless
head -5 ACCOUNTLIB.csv | paste
ACCOUNTLIB
TIDE SCHEDULE
VAT BS/ENC
PAYABLES
ACCOMMODATION GUIDE
我鼓励您自己测试它,但以下语法应该产生相同的结果——在这种情况下,粘贴命令再次毫无用处:
head -5 ACCOUNTLIB.csv | paste -
那么,从标准输入读取数据有什么意义呢?好吧,有了 -s
标志,事情就变得更加有趣了,正如我们现在所看到的那样。
4.1.连接文件的行
正如我们在前面几段中所看到的,在串行模式下,粘贴命令会将输入文件的所有行写入同一输出行。这为我们提供了一种简单的方法将从标准输入读取的所有行连接到一个(可能很长)输出行:
sh$ head -5 ACCOUNTLIB.csv | paste -s -d':'
ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE
这与使用 tr 命令执行的操作基本相同,但有一点不同。让我们使用 diff
实用程序来发现这一点:
sh$ diff <(head -5 ACCOUNTLIB.csv | paste -s -d':') \
<(head -5 ACCOUNTLIB.csv | tr '\n' ':')
1c1
< ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE
---
> ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE:
\ No newline at end of file
根据 diff 实用程序的报告,我们可以看到 tr 命令已用给定的分隔符替换了换行符的每个实例,包括最后一个。另一方面,paste
命令保持最后一个换行符不变。因此,根据您是否需要最后一个字段之后的分隔符,您将使用一个命令或另一个命令。
4.2.一个输入文件的多列格式
根据 Open Group 规范,“标准输入应通过 paste
命令一次读取一行”。因此,将多次出现的 -
特殊文件名作为参数传递给 paste
命令将导致尽可能多的连续输入行被写入同一输出行:
sh$ seq 9 | paste - - -
1 2 3
4 5 6
7 8 9
为了让事情变得更清楚,我鼓励您研究下面两个命令之间的区别。在第一种情况下,粘贴命令会打开同一个文件三次,导致输出中出现数据重复。另一方面,在第二种情况下,ACCOUNTLIB 文件仅打开一次(由 shell),但每行读取三次(由 paste
命令),导致显示文件内容作为三列:
sh$ paste ACCOUNTLIB.csv ACCOUNTLIB.csv ACCOUNTLIB.csv | head -2
ACCOUNTLIB ACCOUNTLIB ACCOUNTLIB
TIDE SCHEDULE TIDE SCHEDULE TIDE SCHEDULE
sh$ paste - - - < ACCOUNTLIB.csv | head -2
ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC
PAYABLES ACCOMMODATION GUIDE VAT BS/ENC
鉴于从标准输入读取时 paste
命令的行为,通常不建议使用多个 -
串行模式下的特殊文件名。在这种情况下,第一次出现将读取标准输入直到其结束,而后续出现的 -
将从已经耗尽的输入流中读取 - 导致不再有可用数据:
# The following command will produce 3 lines of output.
# But the first one exhausted the standard input,
# so the remaining two lines are empty
sh$ seq 9 | paste -s - - -
1 2 3 4 5 6 7 8 9
5. 处理不同长度的文件
paste
实用程序的 Open Group 规范非常明确:
如果在一个或多个输入文件(但不是所有输入文件)上检测到文件结束条件,则粘贴的行为应如同从检测到文件结束的文件中读取空行,除非 -s 选项已指定。
因此,行为正如您所期望的:丢失的数据被“空”内容替换。为了说明这种行为,让我们在“数据库”中记录更多交易。为了保持原始文件完整,我们将处理数据的副本:
# Copy files
sh$ for f in ACCOUNTNUM ACCOUNTLIB CREDIT DEBIT; do
cp ${f}.csv NEW${f}.csv
done
# Update the copy
sh$ cat - << EOF >> NEWACCOUNTNUM.csv
1080
4356
EOF
sh$ cat - << EOF >> NEWDEBIT.csv
00000001207,35
EOF
sh$ cat - << EOF >> NEWCREDIT.csv
00000001207,35
EOF
通过这些更新,我们现在已经注册了从账户 #1080 到账户 #4356 的新资本流动。但是,您可能已经注意到,我没有费心更新 ACCOUNTLIB 文件。这似乎不是一个大问题,因为 paste
命令将用空数据替换丢失的行:
sh$ paste -d';' NEWACCOUNTNUM.csv \
NEWACCOUNTLIB.csv \
NEWDEBIT.csv \
NEWCREDIT.csv | tail
4356;PAYABLES;;00000000402,03
613866;RENTAL COSTS;00000000018,00;
4356;PAYABLES;;00000000018,00
657991;MISCELLANEOUS CHARGES;00000000015,00;
445333;VAT BS/DEBIT;00000000003,00;
4356;PAYABLES;;00000000018,00
626510;LANDLINE TELEPHONE;00000000069,14;
445452;VAT BS/ENC;00000000013,83;
1080;;00000001207,35; # <-- the account label is missing here
4356;;;00000001207,35 # <-- the account label is missing here
但请注意,paste
命令只能按行的物理位置进行匹配:它只能告诉您一个文件比另一个文件“短” 。不是哪里数据丢失。因此,它总是在输出的末尾添加空白字段,这可能会导致数据中出现意外的偏移。让我们通过添加另一笔交易来使这一点变得显而易见:
sh$ cat << EOF >> NEWACCOUNTNUM.csv
4356
3465
EOF
sh$ cat << EOF >> NEWACCOUNTLIB.csv
PAYABLES
WEB HOSTING
EOF
sh$ cat << EOF >> NEWDEBIT.csv
00000000706,48
EOF
sh$ cat << EOF >> NEWCREDIT.csv
00000000706,48
EOF
这次,我更加严格,因为我正确更新了帐号(ACCOUNTNUM)及其相应的标签(ACCOUNTLIB)以及CREDIT和DEBIT数据文件。但由于上一条记录中缺少数据,因此 paste
命令不再能够将相关字段保留在同一行:
sh$ paste -d';' NEWACCOUNTNUM.csv \
NEWACCOUNTLIB.csv \
NEWDEBIT.csv \
NEWCREDIT.csv | tail
4356;PAYABLES;;00000000018,00
657991;MISCELLANEOUS CHARGES;00000000015,00;
445333;VAT BS/DEBIT;00000000003,00;
4356;PAYABLES;;00000000018,00
626510;LANDLINE TELEPHONE;00000000069,14;
445452;VAT BS/ENC;00000000013,83;
1080;PAYABLES;00000001207,35;
4356;WEB HOSTING;;00000001207,35
4356;;;00000000706,48
3465;;00000000706,48;
正如您所看到的,帐户 #4356 报告时带有标签“WEB HOSTING”,而实际上,后者应该出现在与帐户 #3465 对应的行上。
总之,如果您必须处理丢失的数据,您应该考虑使用 join
实用程序而不是 paste
命令,因为后者将根据内容匹配行,而不是基于它们在输入文件中的位置。这使得它更适合“数据库”风格的应用程序。我已经发布了一个有关 join
命令的视频,但这可能值得单独撰写一篇文章,所以如果您对该主题感兴趣,请告诉我们!
6. 循环遍历分隔符
在绝大多数用例中,您将仅提供一个字符作为分隔符。这就是我们迄今为止所做的。但是,如果您在 -d
选项后给出多个字符,则粘贴命令将循环遍历它们:第一个字符将用作行上的第一个字段分隔符,第二个字符将用作第二个字段分隔符等。
sh$ paste -d':+-' ACCOUNT*.csv CREDIT.csv DEBIT.csv | head -5
ACCOUNTLIB:ACCOUNT NUM+CREDIT-DEBIT
TIDE SCHEDULE:623477+-00000001615,00
VAT BS/ENC:445452+-00000000323,00
PAYABLES:4356+00000001938,00-
ACCOMODATION GUIDE:623372+-00000001333,00
字段分隔符只能出现在字段之间。不在一行的末尾。并且您不能在两个给定字段之间插入多个分隔符。作为克服这些限制的技巧,您可以使用 /dev/null
特殊文件作为需要额外分隔符的额外输入:
# Display the opening bracket between the
# ACCOUNTLIB field and the ACCOUNTNUM field, and
# the closing bracket between the ACCOUNTNUM field
# and the empty `/dev/null` field:
sh$ paste -d'()' \
ACCOUNT*.csv /dev/null | head -5
ACCOUNTLIB(ACCOUNTNUM)
TIDE SCHEDULE(623477)
VAT BS/ENC(445452)
PAYABLES(4356)
ACCOMODATION GUIDE(623372)
你甚至可能滥用的东西:
sh$ paste -d'# is ' \
- ACCOUNTNUM.csv - - - ACCOUNTLIB.csv < /dev/null | tail -5
#657991 is MISCELLANEOUS CHARGES
#445333 is VAT BS/DEBIT
#4356 is PAYABLES
#626510 is LANDLINE TELEPHONE
#445452 is VAT BS/ENC
但是,不用说,如果您达到了这种复杂程度,则可能表明 paste
实用程序不一定是完成这项工作的最佳工具。在这种情况下,也许值得考虑其他命令,例如 sed 或 awk 命令。
但是,如果列表包含的分隔符少于在输出中显示行所需的分隔符,该怎么办?有趣的是,paste
命令会“循环”它们。因此,一旦列表用完,paste
命令将跳回到第一个分隔符,这可能为一些创造性的使用打开了大门。就我自己而言,鉴于我的数据,我无法利用该功能做出任何真正有用的事情。所以你必须对下面这个有点牵强的例子感到满意。但这不会完全浪费你的时间,因为这是一个很好的机会来提到当你想使用它作为分隔符时你必须加倍反斜杠(\\
):
sh$ paste -d'/\\' \
- ACCOUNT*.csv CREDIT.csv DEBIT.csv - < /dev/null | tail -5
/MISCELLANEOUS CHARGES\657991/\00000000015,00/
/VAT BS/DEBIT\445333/\00000000003,00/
/PAYABLES\4356/00000000018,00\/
/LANDLINE TELEPHONE\626510/\00000000069,14/
/VAT BS/ENC\445452/\00000000013,83/
7. 多字节字符分隔符
与大多数标准 Unix 实用程序一样,粘贴命令是在一个字符相当于一个字节时诞生的。但情况已不再如此:如今,许多系统默认使用 UTF-8 可变长度编码。在UTF-8中,一个字符可以用1、2、3或4个字节来表示。这使我们能够在同一个文本文件中混合各种人类书写内容以及大量符号和表情符号,同时保持与传统一字节 US-ASCII 字符编码的提升兼容性。
举例来说,我想使用白色钻石 (◇ U+25C7) 作为我的字段分隔符。在 UTF-8 中,该字符使用三个字节 e2 97 87
进行编码。这个字符可能很难从键盘上获得,所以如果你想自己尝试一下,我建议你从下面的代码块中复制粘贴它:
# The sed part is only used as a little trick to add the
# row number as the first field in the output
sh$ sed -n = ACCOUNTNUM.csv |
paste -d'◇' - ACCOUNT*.csv | tail -5
26�MISCELLANEOUS CHARGES�657991
27�VAT BS/DEBIT�445333
28�PAYABLES�4356
29�LANDLINE TELEPHONE�626510
30�VAT BS/ENC�445452
相当具有欺骗性,不是吗?我没有看到预期的白色菱形,而是看到了“问号”符号(至少,这是它在我的系统上显示的方式)。但这不是一个“随机”字符。它是 Unicode 替换字符,用于“当系统无法将数据流呈现为正确的符号时指示问题”。那么,到底出了什么问题呢?
再次检查输出的原始二进制内容将为我们提供一些线索:
sh$ sed -n = ACCOUNTNUM.csv | paste -d'◇' - ACCOUNT*.csv | tail -5 | hexdump -C
00000000 32 36 e2 4d 49 53 43 45 4c 4c 41 4e 45 4f 55 53 |26.MISCELLANEOUS|
00000010 20 43 48 41 52 47 45 53 97 36 35 37 39 39 31 0a | CHARGES.657991.|
00000020 32 37 e2 56 41 54 20 42 53 2f 44 45 42 49 54 97 |27.VAT BS/DEBIT.|
00000030 34 34 35 33 33 33 0a 32 38 e2 50 41 59 41 42 4c |445333.28.PAYABL|
00000040 45 53 97 34 33 35 36 0a 32 39 e2 4c 41 4e 44 4c |ES.4356.29.LANDL|
00000050 49 4e 45 20 54 45 4c 45 50 48 4f 4e 45 97 36 32 |INE TELEPHONE.62|
00000060 36 35 31 30 0a 33 30 e2 56 41 54 20 42 53 2f 45 |6510.30.VAT BS/E|
00000070 4e 43 97 34 34 35 34 35 32 0a |NC.445452.|
0000007a
我们已经有机会练习上面的十六进制转储,因此您的眼睛现在应该足够敏锐,可以发现字节流中的字段分隔符。仔细观察,您会发现行号后面的字段分隔符是字节e2
。但如果您继续调查,您会注意到第二个字段分隔符是 97
。 paste
命令不仅没有输出我想要的字符,而且它也没有在所有地方使用相同的字节作为分隔符?!?
等一下:这难道没有让你想起我们已经讨论过的事情吗?还有那两个字节e2 97
,你是不是有点熟悉?好吧,熟悉可能有点太多了,但如果你跳回几段,你可能会发现它们在某处提到过……
那么你找到它在哪里了吗?之前我说过,在UTF-8中,白色菱形被编码为三个字节e2 97 87
。事实上,paste
命令认为该序列不是一个完整的三字节字符,而是三个独立 字节,因此,它使用第一个字节作为第一个字段分隔符,然后第二个字节作为第二个字段分隔符。
我让您通过在输入数据中再添加一列来重新运行该实验;您应该看到第三个字段分隔符是 87
— 白色菱形的 UTF-8 表示形式的第三个字节。
好的,这就是解释:paste
命令仅接受一字节“字符”作为分隔符。这特别烦人,因为我再一次不知道有什么方法可以克服这个限制,除非使用我已经给你的 /dev/null
技巧:
sh$ sed -n = ACCOUNTNUM.csv |
paste -d'◇' \
- /dev/null /dev/null \
ACCOUNTLIB.csv /dev/null /dev/null \
ACCOUNTNUM.csv | tail -5
26◇MISCELLANEOUS CHARGES◇657991
27◇VAT BS/DEBIT◇445333
28◇PAYABLES◇4356
29◇LANDLINE TELEPHONE◇626510
30◇VAT BS/ENC◇445452
如果您阅读了我之前有关 cut
命令的文章,您可能还记得我对该工具的 GNU 实现也遇到过类似的问题。但我当时注意到 OpenBSD 实现正确地考虑了 LC_CTYPE 区域设置来识别多字节字符。出于好奇,我也在 OpenBSD 上测试了 paste
命令。唉,这次的结果与我的 Debian 机器上的结果相同,尽管 paste
实用程序的规范提到 LC_CTYPE 环境变量来确定 ” 序列解释的语言环境文本数据字节作为字符(例如,参数和输入文件中的单字节而不是多字节字符)”。根据我的经验,paste 实用程序的所有主要实现当前都会忽略分隔符列表中的多字节字符并采用一字节分隔符。但我不会声称已经针对所有 *nix 平台进行了测试。因此,如果我在这里遗漏了什么,请随时使用评论部分来纠正我!
额外提示:避免 \0 陷阱
由于历史原因:
命令:
粘贴-d“\0”... 粘贴-d“”...
不一定等价; IEEE Std 1003.1-2001 本卷未指定后者,可能会导致错误。构造“\0”用于表示“无分隔符”,因为粘贴的历史版本不遵循语法指南,并且命令:
粘贴-d“”...
getopt() 无法正确处理。
因此,不使用分隔符粘贴文件的可移植方法是指定 \0
分隔符。这有点违反直觉,因为对于许多命令来说,\0
表示 NUL 字符——编码为仅由零组成的字节的字符,不应与任何文本内容冲突。
您可能会发现 NUL 字符是一个有用的分隔符,特别是当您的数据可能包含任意字符时(例如使用文件名或用户提供的数据时)。不幸的是,我不知道有什么方法可以通过 paste
命令使用 NUL 字符作为字段分隔符。但也许你知道该怎么做吗?如果是这样的话,我将非常乐意在命令部分阅读您的解决方案。
另一方面,GNU Coreutils 的 paste
实现部分具有非标准的 -z
选项,用于从换行符切换为 NUL 字符作为行分隔符。但在这种情况下,NUL 字符将用作输入和输出的行分隔符两者。因此,为了测试该功能,我们首先需要输入文件的零终止版本:
sh$ tr '\n' '\0' < ACCOUNTLIB.csv > ACCOUNTLIB.zero
sh$ tr '\n' '\0' < ACCOUNTNUM.csv > ACCOUNTNUM.zero
要查看过程中发生了什么变化,我们可以使用 hexdump 实用程序检查文件的原始二进制内容:
sh$ hexdump -C ACCOUNTLIB.csv | head -5
00000000 41 43 43 4f 55 4e 54 4c 49 42 0a 54 49 44 45 20 |ACCOUNTLIB.TIDE |
00000010 53 43 48 45 44 55 4c 45 0a 56 41 54 20 42 53 2f |SCHEDULE.VAT BS/|
00000020 45 4e 43 0a 50 41 59 41 42 4c 45 53 0a 41 43 43 |ENC.PAYABLES.ACC|
00000030 4f 4d 4f 44 41 54 49 4f 4e 20 47 55 49 44 45 0a |OMODATION GUIDE.|
00000040 56 41 54 20 42 53 2f 45 4e 43 0a 50 41 59 41 42 |VAT BS/ENC.PAYAB|
sh$ hexdump -C ACCOUNTLIB.zero | head -5
00000000 41 43 43 4f 55 4e 54 4c 49 42 00 54 49 44 45 20 |ACCOUNTLIB.TIDE |
00000010 53 43 48 45 44 55 4c 45 00 56 41 54 20 42 53 2f |SCHEDULE.VAT BS/|
00000020 45 4e 43 00 50 41 59 41 42 4c 45 53 00 41 43 43 |ENC.PAYABLES.ACC|
00000030 4f 4d 4f 44 41 54 49 4f 4e 20 47 55 49 44 45 00 |OMODATION GUIDE.|
00000040 56 41 54 20 42 53 2f 45 4e 43 00 50 41 59 41 42 |VAT BS/ENC.PAYAB|
我将让您自己比较上面的两个十六进制转储,以识别“.zero”文件和原始文本文件之间的差异。作为提示,我可以告诉您换行符被编码为 0a
字节。
希望您花时间在“.zero”输入文件中找到 NUL 字符。无论如何,我们现在有了输入文件的零终止版本,因此我们可以使用 paste
命令的 -z
选项来处理这些数据,并在输出中生成以及零终止的结果:
# Hint: in the hexadecimal dump:
# the byte 00 is the NUL character
# the byte 09 is the TAB character
# Look at any ASCII table to find the mapping
# for the letters or other symbols
# (https://en.wikipedia.org/wiki/ASCII#Character_set)
sh$ paste -z *.zero | hexdump -C | head -5
00000000 41 43 43 4f 55 4e 54 4c 49 42 09 41 43 43 4f 55 |ACCOUNTLIB.ACCOU|
00000010 4e 54 4e 55 4d 00 54 49 44 45 20 53 43 48 45 44 |NTNUM.TIDE SCHED|
00000020 55 4c 45 09 36 32 33 34 37 37 00 56 41 54 20 42 |ULE.623477.VAT B|
00000030 53 2f 45 4e 43 09 34 34 35 34 35 32 00 50 41 59 |S/ENC.445452.PAY|
00000040 41 42 4c 45 53 09 34 33 35 36 00 41 43 43 4f 4d |ABLES.4356.ACCOM|
# Using the `tr` utility, we can map \0 to newline
# in order to display the output on the console:
sh$ paste -z *.zero | tr '\0' '\n' | head -3
ACCOUNTLIB ACCOUNTNUM
TIDE SCHEDULE 623477
VAT BS/ENC 445452
由于我的输入文件的数据中不包含嵌入的换行符,因此 -z
选项在这里的用处有限。但基于上面的解释,我让您尝试理解为什么下面的示例“按预期”工作。要充分理解,您可能需要下载示例文件并使用 hexdump
实用程序在字节级别检查它们,就像我们上面所做的那样:
# Somehow, the head utility seems to be confused
# by the ACCOUNTS file content (I wonder why?;)
sh$ head -3 CATEGORIES ACCOUNTS
==> CATEGORIES <==
PRIVATE
ACCOMMODATION GUIDE
SHARED
==> ACCOUNTS <==
6233726230846265106159126579914356613866618193623477623795445333445452605751
# The output is quite satisfactory, putting the account number
# after the account name and keeping things surprisingly nicely formatted:
sh$ paste -z -d':' CATEGORIES ACCOUNTS | tr '\0' '\n' | head -5
PRIVATE
ACCOMMODATION GUIDE:623372
SHARED
ADVERTISEMENTS:623084
更重要的是?
paste
命令仅生成分隔文本输出。但如介绍视频末尾所示,如果您的系统支持 BSD column
实用程序,您可以使用它通过转换 paste
命令输出来获取格式良好的表格为固定宽度的文本格式。但这将是即将发表的文章的主题。因此,请继续关注,并且一如既往,不要忘记在您最喜欢的网站和社交媒体上分享该文章!