与 pax 和平共处

pax 是典型 Linux 安装中不太为人所知的实用程序之一。这太可惜了,因为 pax 具有非常好的功能集,并且其命令行选项易于理解和记忆。pax 是一个归档工具,类似于 tar(1),但在某些方面它也是 cp(1) 的更好版本,尤其因为您可以将 pax 与 SSH 结合使用,以通过网络复制文件集。一旦您学会了 pax,您可能会想知道这些年没有它是怎么过来的。

pax 有四种模式:list(列表)、read(读取)、write(写入)和 copy(复制)。读取和写入分别由 -r-w 选项控制。组合使用 -rw 时,pax 的行为有点像 cp -R。如果两者都不使用,pax 会列出归档文件的内容,该归档文件可以是文件、设备或管道。

默认情况下,pax 作为过滤器运行:它从标准输入读取并写入标准输出,这是一个非常有用的功能。但通常在今天,目标是一个归档文件,即熟悉的 tarball。让我们从创建一个开始


$ cd /tmp
$ mkdir paxample
$ touch paxample/foo
$ pax -wf paxample.tar paxample

-w 选项表示 write(写入)——即创建归档文件。-f 选项提供要写入归档文件的文件名。如果需要,pax 可以同时 gzip 或 bzip 文件


$ pax -wzf paxample.tar.gz paxample

像大多数 tar 实现一样,默认情况下,pax 使用 Posix ustar 文件格式。由于 pax 的诞生源于统一归档文件格式的愿望,因此也支持许多其他格式,但在实践中,它们很少使用。很可能,您从 Internet 下载的任何 .tar.gz 文件实际上都将是一个 ustar 归档文件


$ pax -wzf paxample.tar.gz paxample
$ file paxample.tar*
paxample.tar:    POSIX tar archive
paxample.tar.gz: gzip compressed data

您几乎总是想知道任何归档文件的第一件事是其中包含什么。在没有 -r-w 选项的情况下,列出内容是默认操作


$ pax -f paxample.tar
paxample
paxample/foo

请注意,归档文件保留您在命令行中指定的目录名称。这在稍后读取它时会发挥作用。

要读取归档文件,请使用 -r


$ mkdir t
$ cd t
$ pax -rf ../paxample.tar

它做了什么?让我们看一下源目录和目标目录


$ cd /tmp
$ find paxample t # traverse both trees
paxample
paxample/foo
t
t/paxample
t/paxample/foo

当 pax 读取 paxample.tar 归档文件时,它在当前目录 t 中创建了文件。由于该归档文件包含目录名称 paxample,因此该目录在输出中被重新创建。

复制文件集

在我看来,pax 的 -r-w 选项比 tar 中等效的 -x-c 选项更有意义——这足以成为切换的理由。但是,pax 可以做更多的事情,而不仅仅是 tar:它也可以复制文件


$ rm -rf t
$ pax -rw paxample t
$ find t
t
t/paxample
t/paxample/foo

与 cp(1) 不同,pax 是一个归档实用程序。它的工作不是制作副本,而是归档文件。当 pax 创建文件时,它会保留来自其输入的文件的元数据。输入的格式无关紧要。在本例中,输入不是来自归档文件,而是文件本身


$ ls -l paxample/foo t/paxample/foo
-rw-r--r--  1 jklowden  wheel  0 Sep 22 15:45 paxample/foo
-rw-r--r--  1 jklowden  wheel  0 Sep 22 15:45 t/paxample/foo

是的——两个相同的文件具有两个相同的时间戳。如果需要,也可以控制权限位和所有权。接受吧,cp(1)!

也许您不想重新创建目录,或者也许您想以某种方式更改它。一种选择是在命令行上不提及输入目录,而是提供文件名


$ rm -rf t/paxample/
$ (cd paxample/ && pax -rw * ../t/)
$ find t
t
t/foo

这通常是最容易的。但是,如果您需要更复杂的功能,-s 选项会使用正则表达式重写路径——实际上是文件名的任何部分


$ rm -rf t/*
$ pax -rw -s ':paxample:my/new/path:g' paxample/ t
$ find t
t
t/my
t/my/new
t/my/new/path
t/my/new/path/foo

例如,当解压缩 tarball 且目录名称中没有版本信息时,-s 选项非常方便。

可能出现什么问题?

如果您提供了错误的文件名进行写入,您只会得到一个名称错误的归档文件——无伤大雅。但是,如果您错误地输入了输入归档文件名,您会发现自己回到了 1985 年


$ pax -rf paxample.whoopsie
pax: Failed open to read on paxample.whoopsie (No such file 
or directory)

ATTENTION! pax archive volume change required.
Ready for archive volume: 1
Input archive name or "." to quit pax.
Archive name >

这是一个在实施之前就已过时的想法。您可以在此处再次键入文件名,而无需 readline 支持或 Tab 键补全。好吧,至少它说明了该怎么做


Archive name > .
Quitting pax!

多么令人兴奋!

如前所述,默认情况下,pax 使用标准输入和标准输出。这确实是一个功能,但当您第一次忘记提供文件名时,您可能会认为 pax 非常非常慢


$ pax  -r paxample.tar

哎呀!没有 -f。也没有消息和提示。pax 正在忽略归档文件名参数并读取标准输入,在本例中,它是键盘。您可以键入 ^D 表示文件结束,但这会形成对 pax 的无效输入。最好发出烟雾信号


^C
pax: Signal caught, cleaning up.

当您第一次在标准输出连接到终端时意外写入标准输出时,情况会更糟。您是第一个听到这句话的人:不要那样做。

将标准输入投入使用

标准输入和标准输出确实有它们的用途,而这正是 pax 真正发挥作用的地方。首先,您可以验证 -s 选项的效果,而无需创建归档文件或文件


$ pax -w -s ':paxample:my/new/path:g' paxample/ | pax
my/new/path
my/new/path/foo

在没有 -f 选项的情况下,pax -w 会写入标准输出。因此,使用 -s 重写路径名,并将输出通过管道再次传输到 pax,这次使用其 list 模式,既不使用 -r 也不使用 -w 选项。默认情况下,pax 从标准输入读取,并在 list 模式下,在终端上打印文件名。

当有数千个文件时,这可以节省大量时间,更不用说磁盘上的混乱了。

假设您想将 paxample 目录复制到另一台机器。一种方法是创建一个 tarball,复制到目标机器,登录到目标机器并解压缩 tarball


$ pax -wf paxample.tar paxample
$ scp paxample.tar oak:/tmp/
paxample.tar             100%   10KB  10.0KB/s   00:00
$ ssh oak
oak[~]$ cd /tmp
oak[tmp]$ pax -rf paxample.tar
oak[tmp]$ ls paxample/
foo

但是还有一种更简单的方法。在两台机器上调用 pax,并将一台机器的输出连接到另一台机器的输入


$ pax -w paxample | ssh oak 'cd /tmp/ && pax -r && find paxample'
paxample
paxample/foo

pax -w 写入标准输出。ssh 读取标准输入并将其附加到调用的任何实用程序,当然在本例中再次是 pax。pax -r 从标准输入读取并从该归档文件创建文件。

pax 是典型 Linux 安装中不太为人所知的实用程序之一。但它既简单又通用,非常值得花时间学习——推荐。

加载 Disqus 评论