将一个目录打包成tar文件,但不要在归档中存储完整的绝对路径。

457

我在备份脚本的一部分中有以下命令:

tar -cjf site1.bz2 /var/www/site1/

当我列出存档的内容时,我得到:

tar -tf site1.bz2
var/www/site1/style.css
var/www/site1/index.html
var/www/site1/page2.html
var/www/site1/page3.html
var/www/site1/images/img1.png
var/www/site1/images/img2.png
var/www/site1/subdir/index.html

但我希望从存档中的目录和文件名中删除/var/www/site1部分,以简化提取并避免无用的常量目录结构。万一我在一个不存储Web数据在/var/www下的地方解压备份的网站。

对于上面的示例,我想要:

tar -tf site1.bz2
style.css
index.html
page2.html
page3.html
images/img1.png
images/img2.png
subdir/index.html

因此,当我解压缩文件时,文件将被解压缩到当前目录中,我不需要在之后移动解压缩文件,并且子目录结构得以保留。

关于tar和备份,stackoverflow及其他网站已经有很多相关问题,但它们大多数要求删除整个子目录结构(平铺),或者仅添加或删除名称中的初始/(我不知道在解压缩时会发生什么变化),但没有更多的信息。

在阅读了一些在这里和其他地方找到的解决方案以及手册之后,我尝试了以下方法:

tar -cjf site1.bz2 -C . /var/www/site1/
tar -cjf site1.bz2 -C / /var/www/site1/
tar -cjf site1.bz2 -C /var/www/site1/ /var/www/site1/
tar -cjf site1.bz2 --strip-components=3 /var/www/site1/
但它们都没有按照我想要的方式工作。有些什么也没做,有些则不再存档子目录。
这是在由Cron启动的备份shell脚本中,所以我不知道运行它的用户是谁,路径和当前目录是什么,因此对于所有事情始终需要编写绝对路径,并且最好不更改当前目录,以避免在脚本中进一步破坏某些内容(因为它不仅备份网站,还备份数据库,然后将所有内容发送到FTP等)。
如何实现这一点?
我是否误解了选项-C的工作原理?

1
相关链接:https://unix.stackexchange.com/questions/22842/tar-with-relative-paths | https://serverfault.com/questions/339762/how-to-tarball-a-directory-without-carrying-over-path-to-folder如何使用相对路径打包tar文件?如何在不携带文件夹路径的情况下打包一个目录? - Ciro Santilli OurBigBook.com
1
嗯,“-C”只是表示“更改目录”,而替换路径(或前缀)只能通过“--transform”来完成。参考:https://superuser.com/questions/595510/prepend-prefix-in-tar/595512您可以简单地使用“-C”(更改目录)和“--transform”进行操作:tar cjf site1.bz2 --transform "s/^\.\//$targetbase/" -C /var/www/site1 . - Daniele Cruciani
这是一个非常好的问题,可悲的是到目前为止没有一个令人满意的答案。我们还没有听到一些明智的人如何可能在没有任何参考原始位置或目录树的情况下,仅提取单个文件style.css(上面的示例)到当前目录中?我不想用不需要的新树结构混乱我的当前目录。听起来像是tarball多年来被忽视的严重缺陷。 - user9224371
1
@elmclose,你误解了问题。OP想要创建一个存档,而不是提取一个存档。 - Sod Almighty
9个回答

580
tar -cjf site1.tar.bz2 -C /var/www/site1 .
在上面的例子中,tar会在执行操作前切换到目录/var/www/site1,因为给定了选项-C /var/www/site1
来自man tar:
OTHER OPTIONS

  -C, --directory DIR
       change to directory DIR

228
不要错过末尾的句点,那很重要;-) - Freedom_Ben
15
如果您也希望根据通配符选择要备份的文件,怎么办?-C /var/www/site1 *.dat是不起作用的 :( - Andy Lorenz
25
点号表示 tar 压缩当前目录中的所有文件。而 -C 则设置当前目录。 - Lars Brinkhoff
32
这非常有效。我发现保留目录名称(而不是完整路径)很有用,所以我采取了以下措施: tar -czvf site1.tar.gz -C /var/www/ site1 (注意空格,我仍然使用-C切换到父目录,并指定要打包的目录而不是点) - jorfus
39
在tar路径中我得到了一个前导点,例如./folders,如何去掉它? - Mika571
显示剩余9条评论

67

选项-C是有效的;仅为澄清,我将发布两个示例:

  1. 创建一个没有完整路径的tar包: 完整路径为/home/testuser/workspace/project/application.war,我们只需要的是project/application.war, 因此:

tar -cvf output_filename.tar  -C /home/testuser/workspace project
注意:workspaceproject之间有一个空格;tar将仅使用project替换完整路径。

  • 提取tarball并更改目标路径(默认为 . ,即当前目录)

    tar -xvf output_filename.tar -C /home/deploy/
    

    tar会根据给定的路径提取tarball并保留创建路径;在我们的示例中,文件application.war将被提取到/home/deploy/project/application.war

    /home/deploy: 提取时给定的路径
    project: 创建tarball时给定的路径

  • 注意:如果您想将创建的tarball放置在目标目录中,只需在tarball名称之前添加目标路径。例如:

    tar -cvf /path/to/place/output_filename.tar  -C /home/testuser/workspace project
    

    3
    如何在最后一个示例中添加通配符以进行文件选择? - Siva
    通配符的问题在于,shell会将它们扩展为匹配的文件名,而如果它们被引用,tar则不会扩展它们... - Gert van den Berg
    1
    我在Ubuntu 18.04上尝试过这个,但没有运气。我不确定我错过了什么。当我打包时,我的stdout正确地显示它,但是当我解压缩时,它仍然具有完整的路径。 - sdc

    23

    看起来在tar v2.8.3及之前的版本中,-C选项不会在所有平台(操作系统)上一致地工作。据说-C选项会将目录添加到归档文件中,但在Mac和Ubuntu上,它会在生成的tar.gz文件内部添加绝对路径前缀。

    tar target_path/file.tar.gz -C source_path/source_dir
    
    因此,一致而强大的解决方案是cd到source_path(source_dir的父目录),然后运行。
    tar target_path/file.tar.gz source_dir
    
    或者
    tar -cf target_path/file.tar.gz source_dir
    
    在你的脚本中添加这行代码。它将会移除生成的 tar.gz 文件的目录结构中的绝对路径前缀。

    1
    使用-C选项确实会在Fedora 29上生成的tar.gz文件中移除绝对路径前缀。你的答案是否针对某个系统? - EL_DON
    @EL_DON:我没有在 Fedora 上测试 -C 选项,但理论上 tar 应用程序软件应该在每个平台上都能正常工作,除非这是 tar 应用程序中的 bug。我已在 Mac 10.8、Mac 10.13 和 Ubuntu(版本我记不清了)上测试了 -C 选项。但自从 tar v2.8.3 版本以来,命令已更改为 tar -cf 目标路径/文件.tar.gz 源目录 如果你添加 -C 选项,它仍然不会移除生成的 tar.gz 文件中的绝对路径前缀。 - Chinthaka Senanayaka
    我在CentOS系统上进行了再次测试。在创建示例中的所有路径并运行命令(在“tar”后添加“-cvf”)之后,我发现生成的tar.gz文件内部没有绝对路径,这与其他答案一致。如果您认为我用于测试的两个系统上的tar已经损坏或过时,请提供一些支持您答案的文档链接。我认为“-C”选项在执行之前更改目录(如其他答案中所述)。当我省略它时,tar会尝试从“./”添加垃圾,包括从“./”开始的路径。 - EL_DON
    1
    我使用了这个文档:https://linux.die.net/man/1/tar是的,文档说-C可以改变路径,但在我的Mac 10.13上它不起作用。这可能是tar应用程序的不一致行为。这意味着这是一个错误。如果您正在编写一个要在所有Unix平台上运行的shell脚本,则最好使用能在所有操作系统上工作的代码来确保安全。 - Chinthaka Senanayaka
    你的回答没有提到可能存在错误,而跨平台兼容性更健壮的解决方案是先执行 cd 命令。你的回答声称该工具的工作方式与文档和我的系统相反,因此是错误的答案。你可以轻松地修复它。 - EL_DON
    显示剩余2条评论

    19

    一个小细节:

    tar -cjf site1.tar.bz2 -C /var/www/site1 .
    

    将文件添加

    tar -tf site1.tar.bz2
    ./style.css
    ./index.html
    ./page2.html
    ./page3.html
    ./images/img1.png
    ./images/img2.png
    ./subdir/index.html
    

    如果你真的想要。

    tar -tf site1.tar.bz2
    style.css
    index.html
    page2.html
    page3.html
    images/img1.png
    images/img2.png
    subdir/index.html
    

    您应该首先进入目录

    • 或者运行
    tar -cjf site1.tar.bz2 -C /var/www/site1 $(ls -A /var/www/site1)
    

    请注意,这不支持空格。感谢@dragon788和@Fonic。


    4
    如果你使用ls -A命令,你可以获得隐藏文件(即以句点开头的文件),而无需访问...目录,这是在执行tar或rsync命令时尝试解决符号链接时经常造成困惑的普遍来源。 - dragon788
    这将无法处理包含空格的文件夹/文件夹的文件夹。 - Fonic

    13
    以下命令将创建一个根目录“.”并将指定目录中的所有文件放入其中。
    tar -cjf site1.tar.bz2 -C /var/www/site1 .
    

    如果你想把所有文件都放在tar文件的根目录下,@chinthaka是正确的。只需进入目录并执行:

    tar -cjf target_path/file.tar.gz *
    

    这将把当前工作目录中的所有文件作为根文件放入 tar 文件中。


    3
    使用 * 不会保存任何“隐藏”的 .文件或者.文件夹。(顺便提一下,同时使用 -C 和 * 会失败,因为 shell 会扩展当前目录而不是 -C 目录)。 - Xen2050

    5
    使用“点”会在Ubuntu 16上创建一个名为“point”的文件夹。
    tar -tf site1.bz2 -C /var/www/site1/ .
    

    我会更详细地处理这个问题并提供一个例子。多行记录,以及异常情况。

    tar -tf site1.bz2\
        -C /var/www/site1/ style.css\
        -C /var/www/site1/ index.html\
        -C /var/www/site1/ page2.html\
        -C /var/www/site1/ page3.html\
        --exclude=images/*.zip\
        -C /var/www/site1/ images/
        -C /var/www/site1/ subdir/
    /
    

    1
    为什么你称它为“point”?它只是一个点(.),代表当前目录。在tar.gz的结构中,这只是基础/根/顶层,对吗? - EL_DON
    请查看详细信息的快照 image。我认为我的方法更正确,这是我的观点。 - Sergey Asachev

    3
    如果您想归档一个子目录并削减子目录路径,这个命令将非常有用:
    tar -cjf site1.bz2 -C /var/www/ site1
    

    3
    tar -cjf site1.tar.bz2 -C /var/www/site1 --strip-components 1 .
    

    这是与之前所有回复不同的变体,它生成了一个包含以下内容的tar文件:
    style.css
    index.html
    page2.html
    page3.html
    images/img1.png
    images/img2.png
    subdir/index.html
    
    • 使用-C在添加文件之前切换到目录
    • 使用--strip-components 1从所有条目中删除前导的./

    0

    发现tar -cvf site1-$seqNumber.tar -C /var/www/ site1比第二个解决方案tar -cvf site1-$seqNumber.tar -C /var/www/site1 . (注意第二个解决方案中的)更友好,原因如下:

    • Tar文件名可以不重要,因为原始文件夹现在是一个存档条目
    • Tar文件名对内容不重要,现在可以用于其他目的,如序列号、定期备份等

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接