我正在寻找一种通过 STDIN 传递任意数据的方法,并将其像普通文件一样进行压缩。
我试过这个
$ echo test | tar -cf test.tar /dev/stdin
这给了我一个test.tar
包含内容的文件dev/stdin
。该stdin
文件是 的链接/proc/self/fd/0
。
我想要的是dev/stdin
TAR 文件内部是一个包含文本的常规文件test
。类似的情况也发生在:
$ tar -cf test.tar <(echo test)
但名称不同。
这可行吗?
答案1
我认为你不能在这里做你想做的事。您的方法的问题在于tar
处理文件和目录树,而您没有为其提供如下命令:
$ echo test | tar -cf test.tar /dev/sdtin
即使您尝试使用如下子 shell 将字符串“包装”在临时文件中:
$ tar -cf test.tar <(echo test)
您可以看到您的内容仍在使用这些临时文件描述符进行 TAR 压缩:
$ tar tvf test.tar
lr-x------ vagrant/vagrant 0 2018-08-04 23:52 dev/fd/63 -> pipe:[102734]
如果您只想压缩字符串,则需要将它们放入文件上下文中。所以你需要做这样的事情:
$ echo "test" > somefile && tar -cf /tmp/test.tar somefile
您可以看到该文件存在于 TAR 文件中:
$ tar tvf /tmp/test.tar
-rw-rw-r-- vagrant/vagrant 5 2018-08-05 00:00 somefile
使用 tar 复制数据
大多数使用 Unix 多年的人可能都见过这种模式:
$ (cd /; tar cf - .)|(cd /mnt/newroot; tar pxvf -)
我过去一直使用它来将数据从一个位置复制到另一个位置。您也可以通过 SSH 使用它。其他方法在 U&L Q&A 中讨论,标题为:使用 busybox 克隆根目录树。
备份 /
如果您打算备份整个硬盘,还可以使用此方法:
$ sudo tar -cvpzf backup.tar.gz --exclude=/backup.tar.gz --one-file-system /
参考
答案2
如果您知道要传输的内容的最终大小(以字节为单位),则可以将 tar 文件创建为流。然后,需要在数据流之前创建初始 512 字节标头,并在其之后创建尾部填充。尽管它确实取决于tar
文件的确切变体,因为有几种不同的文件格式。以下步骤可能适合您:
创建与数据流大小相同的空洞文件
$ dd if=/dev/zero of=mystream bs=1 count=0 seek=$SIZE
请注意,
mystream
现在它是一个“洞”文件,实际上并不占用磁盘空间,即使它看起来与数据流具有相同的大小 $SIZE。用于为指定大小的
tar
文件创建标头mystream
$ tar cf - mystream | head -c 512 > header
请注意,
tar
将希望创建一个没有漏洞的文件,因此丢弃除前 512 字节之外的所有内容非常重要。然后删除洞文件,以免被意外复制。$ rm mystream
创建一个填充尾部以填充 512 个块。
$ head -c $((echo "scale=0; 512 - $SIZE % 512" | bc )) > trailer
制作带有标头和标尾的 $STREAM(大小为 $SIZE)的 tar 流
$ cat header $STREAM trailer
请注意,您可能希望将其流式传输到标准输出以外的其他地方。
答案3
使用zsh
,您可以使用=(...)
进程替换的形式,它使用临时文件而不是管道。使用 GNUtar
的--transform
选项,您可以更改存档成员的名称:
tar --transform='s/.*/test-file/' -zcf file.tar.gz =(echo test)
将创建一个 file.tar.gz
包含一名test-file
具有权限 0600 和 content 的成员的test\n
.
使用bash
(或zsh
),您可以执行以下操作:
tar --transform='s/.*/test-file/' -zchf file.tar.gz /dev/stdin <<< "$(echo test)"
Here-strings 也在这些 shell 中使用临时文件。但请注意,命令替换会删除每一个尾随换行符并添加一back 和 inbash
会剥离 NUL 字节。