我怎样才能让 tar 从 stdin 读取输入?

我怎样才能让 tar 从 stdin 读取输入?

我需要tar在 shell 脚本中的管道中使用来存档和压缩一些文件。

阅读完手册页tar我能够通过与参数一起运行tar来使输出为,但我无法找到如何使它从中输入。由于许多命令在没有指定其他输入的情况下从中读取,因此我尝试了:stdout-Ostdinstdin

pv ~/foo | tar c -O ~/foo.tar

但这不起作用:

tar: Cowardly refusing to create an empty archive

Try 'tar --help' or 'tar --usage' for more information.

我怎样才能tar从中进行读取输入stdin

答案1

使用-Owithc将被忽略,因为没有文件提取。无论哪种方式,您都会得到相同的结果:

$ tar c -O foo | tar t
foo
$ tar c foo | tar t  
foo

这就是为什么我发现你的错误令人惊讶,因为tar如果你指定了一条路径,就没有任何胆怯。

tar 不能从标准输入读取文件数据来创建档案,从那时起tar就无法知道文件是什么——文件从哪里开始或结束,它的路径和元数据是什么。

答案2

这个 Python 脚本来自我的Unix.StackExchange 答案将 stdin 附加到 tar 文件,并为其指定任意名称,使用Python 的 tarfile库。它会在 tar 中寻找,以在 eof 处用正确的大小重写标头。用法如下:

cat foo | tarappend -t mytar -f foo
tar tvf mytar

以下是tarappendPython 脚本:

#!/usr/bin/python
# concat stdin to end of tar file, with given name. meuh on stackexchange
# $Id: tarappend,v 1.3 2015/07/08 11:31:18 meuh $

import sys, os, tarfile, time, copy
from optparse import OptionParser
try:
    import grp, pwd
except ImportError:
    grp = pwd = None

usage = """%prog: ... | %prog -t tarfile -f filename
Appends stdin to tarfile under the given arbitrary filename.
tarfile is created if it does not exist.\
"""

def doargs():
    parser = OptionParser(usage=usage)
    parser.add_option("-f", "--filename", help="filename to use")
    parser.add_option("-t", "--tarfile", help="existing tar archive")
    (options, args) = parser.parse_args()
    if options.filename is None or options.tarfile is None:
        parser.error("need filename and tarfile")
    if len(args):
        parser.error("unknown args: "+" ".join(args))
    return options

def copygetlen(fsrc, fdst):
    """copy data from file-like object fsrc to file-like object fdst. return len"""
    totlen = 0
    while 1:
        buf = fsrc.read(16*1024)
        if not buf:
            return totlen
        fdst.write(buf)
        totlen += len(buf)

class TarFileStdin(tarfile.TarFile):
    def addstdin(self, tarinfo, fileobj):
        """Add stdin to archive. based on addfile() """
        self._check("aw")
        tarinfo = copy.copy(tarinfo)
        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
        bufoffset = self.offset
        self.fileobj.write(buf)
        self.offset += len(buf)

        tarinfo.size = copygetlen(fileobj, self.fileobj)
        blocks, remainder = divmod(tarinfo.size, tarfile.BLOCKSIZE)
        if remainder > 0:
            self.fileobj.write(tarfile.NUL * (tarfile.BLOCKSIZE - remainder))
            blocks += 1
        self.offset += blocks * tarfile.BLOCKSIZE
        # rewrite header with correct size
        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
        self.fileobj.seek(bufoffset)
        self.fileobj.write(buf)
        self.fileobj.seek(self.offset)
        self.members.append(tarinfo)

class TarInfoStdin(tarfile.TarInfo):
    def __init__(self, name):
        if len(name)>100:
            raise ValueError(name+": filename too long")
        if name.endswith("/"):
            raise ValueError(name+": is a directory name")
        tarfile.TarInfo.__init__(self, name)
        self.size = 99
        self.uid = os.getuid()
        self.gid = os.getgid()
        self.mtime = time.time()
        if pwd:
            self.uname = pwd.getpwuid(self.uid)[0]
            self.gname = grp.getgrgid(self.gid)[0]

def run(tarfilename, newfilename):
    tar = TarFileStdin.open(tarfilename, 'a')
    tarinfo = TarInfoStdin(newfilename)
    tar.addstdin(tarinfo, sys.stdin)
    tar.close()

if __name__ == '__main__':
    options = doargs()
    run(options.tarfile, options.filename)

相关内容