【问题标题】:Find files and tar them (with spaces)查找文件并将它们 tar(带空格)
【发布时间】:2011-08-19 00:15:24
【问题描述】:

好的,这里的问题很简单。我正在研究一个简单的备份代码。它工作正常,除非文件中有空格。这就是我查找文件并将它们添加到 tar 存档的方式:

find . -type f | xargs tar -czvf backup.tar.gz 

问题是文件名中有空格,因为 tar 认为它是一个文件夹。基本上有没有一种方法可以在 find 的结果周围添加引号?或者用不同的方法来解决这个问题?

【问题讨论】:

  • 使用find ... | xargs ... 的最佳方式是在每个参数上使用-print0/-0 参数:find -print0 ... | xargs -0 ...。这将导致文件名被空字符分隔,这意味着您的文件名中可以​​有空格或换行符或其他奇怪的东西,它仍然可以工作。
  • 这样使用xargs和tar会出现问题,当你有大量文件时,xargs会反复调用tar -c,会一直覆盖你的存档,结果你赢了'没有您期望的所有文件。请参阅下面的this more detailed explanationmy answer

标签: linux find backup tar


【解决方案1】:

为什么不:

tar czvf backup.tar.gz *

使用 find 然后使用 xargs 确实很聪明,但这样做很困难。

更新:Porges 评论了一个我认为比我的答案或其他答案更好的查找选项:find -print0 ... | xargs -0 ....

【讨论】:

  • 我的完整代码只会备份过去一天修改过的项目。由于它是每日备份,我不想重复信息以节省文件大小(我也每 15 天进行一次完整备份)。
  • 为了使它成为一个更好的 SO 问题,我会问关于“可靠地同时使用 find、xargs 和 tar”的问题。您的标题和问题并未真正指定您需要 find 和 xargs,但您确实需要。
  • 如果文件列表太长,xargs ... tar c ... 将覆盖创建的第一个存档,xargs 将第二次执行tar!为避免覆盖,您可以使用 xargs -x 但存档可能不完整。替代方案可能是首先tar c ...,然后可能重复tar r ...。 (我对可靠性的贡献:)
【解决方案2】:

尝试运行:

    find . -type f | xargs -d "\n" tar -czvf backup.tar.gz 

【讨论】:

    【解决方案3】:

    使用这个:

    find . -type f -print0 | tar -czvf backup.tar.gz --null -T -
    

    它会:

    • 处理带有空格、换行符、前导破折号和其他有趣内容的文件
    • 处理无限数量的文件
    • 不会像使用 tar -cxargs 那样重复覆盖您的 backup.tar.gz,这样您就有大量文件

    另见:

    【讨论】:

    • 如果您想先通过 sed 管道查找几次,您会怎么做?例如找 。 -print0 | sed /备份/d |焦油....
    • 注意,如果有多个条件需要加括号。否则,-print0 仅适用于最后一个表达式。例如。 find . \( -type f -o -name '*.c' \) -print0 | ...
    • 为了好玩,这里是使用 cygwin 的 Windows 版本:c:\cygwin\bin\find . -regextype posix-egrep -regex '.*(sln^|vcxproj^|filters)$' -print0 | c:\cygwin\bin\tar -cvf MS_Projects.tar --null -T -
    • @Steve 你能解释一下tar命令末尾的'-'选项是什么。我在 GNU tar 的手册页中找不到它。
    • 当然,它是-T 的参数,这意味着从标准输入读取文件名:如果你给一个破折号作为`--files-from' 的文件名,(即,您指定 --files-from=- 或 -T -),然后从标准输入读取文件名
    【解决方案4】:

    可能还有另一种方法可以实现您想要的。基本上,

    1. 使用find 命令输出您要查找的任何文件的路径。将 stdout 重定向到您选择的文件名。
    2. 然后使用 -T 选项进行 tar,该选项允许它获取文件位置列表(您刚刚使用 find 创建的位置!)

      find . -name "*.whatever" > yourListOfFiles
      tar -cvf yourfile.tar -T yourListOfFiles
      

    【讨论】:

    【解决方案5】:

    最好的解决方案似乎是创建一个文件列表,然后归档文件,因为您可以使用其他来源并对列表执行其他操作。

    例如,这允许使用列表来计算正在归档的文件的大小:

    #!/bin/sh
    
    backupFileName="backup-big-$(date +"%Y%m%d-%H%M")"
    backupRoot="/var/www"
    backupOutPath=""
    
    archivePath=$backupOutPath$backupFileName.tar.gz
    listOfFilesPath=$backupOutPath$backupFileName.filelist
    
    #
    # Make a list of files/directories to archive
    #
    echo "" > $listOfFilesPath
    echo "${backupRoot}/uploads" >> $listOfFilesPath
    echo "${backupRoot}/extra/user/data" >> $listOfFilesPath
    find "${backupRoot}/drupal_root/sites/" -name "files" -type d >> $listOfFilesPath
    
    #
    # Size calculation
    #
    sizeForProgress=`
    cat $listOfFilesPath | while read nextFile;do
        if [ ! -z "$nextFile" ]; then
            du -sb "$nextFile"
        fi
    done | awk '{size+=$1} END {print size}'
    `
    
    #
    # Archive with progress
    #
    ## simple with dump of all files currently archived
    #tar -czvf $archivePath -T $listOfFilesPath
    ## progress bar
    sizeForShow=$(($sizeForProgress/1024/1024))
    echo -e "\nRunning backup [source files are $sizeForShow MiB]\n"
    tar -cPp -T $listOfFilesPath | pv -s $sizeForProgress | gzip > $archivePath
    

    【讨论】:

    • 一个衬里?
    【解决方案6】:

    如果您有多个文件或目录,并且您想将它们压缩到独立的*.gz 文件中,您可以这样做。可选-type f -atime

    find -name "httpd-log*.txt" -type f -mtime +1 -exec tar -vzcf {}.gz {} \;
    

    这将压缩

    httpd-log01.txt
    httpd-log02.txt
    

    httpd-log01.txt.gz
    httpd-log02.txt.gz
    

    【讨论】:

      【解决方案7】:

      为什么不试试这样的东西:tar cvf scala.tar `find src -name *.scala`

      【讨论】:

        【解决方案8】:

        here的另一种解决方案:

        find var/log/ -iname "anaconda.*" -exec tar -cvzf file.tar.gz {} +
        

        【讨论】:

          【解决方案9】:

          会向@Steve Kehlet post 添加评论,但需要 50 个代表 (RIP)。

          对于通过大量谷歌搜索找到这篇文章的任何人,我找到了一种方法,不仅可以找到给定时间范围的特定文件,而且不包括会导致 tarring 错误的相对路径或空格。 (非常感谢史蒂夫。)

          find . -name "*.pdf" -type f -mtime 0 -printf "%f\0" | tar -czvf /dir/zip.tar.gz --null -T -
          
          1. .相对目录

          2. -name "*.pdf" 查找 pdf(或任何文件类型)

          3. -type f 要查找的类型是文件

          4. -mtime 0 查找过去 24 小时内创建的文件

          5. -printf "%f\0" 常规 -print0-printf "%f" 不适合我。来自手册页:

          此引用的执行方式与 GNU ls 相同。这与用于 -ls 和 -fls 的引用机制不同。如果您能够决定 find 的输出使用什么格式,那么使用 '\0' 作为终止符通常比使用换行符更好,因为文件名可以包含空格和换行符。

          1. -czvf 创建存档,通过 gzip 过滤存档,详细列出处理的文件,存档名称

          编辑 2019-08-14: 我想补充一点,我也可以在评论中使用相同的命令,只使用 tar 本身:

          tar -czvf /archiveDir/test.tar.gz --newer-mtime=0 --ignore-failed-read *.pdf
          

          如果今天没有新的 PDF,需要--ignore-failed-read

          【讨论】:

            【解决方案10】:

            对几个解决方案(以及您自己的测试)的重大警告:

            当你做:任何事情 | xargs 的东西

            xargs 将尝试在“某物”之后放置“尽可能多的参数”,但最终您可能会多次调用“某物”。

            所以你的尝试:find ... | xargs tar czvf 文件.tgz 在 xargs 每次调用“tar”时可能最终会覆盖“file.tgz”,而您最终只会得到最后一次调用! (选择的解决方案使用 GNU -T 特殊参数来避免该问题,但并非每个人都有可用的 GNU tar)

            你可以这样做:

            find . -type f -print0 | xargs -0 tar -rvf backup.tar
            gzip backup.tar
            

            cygwin 上的问题证明:

            $ mkdir test
            $ cd test
            $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs touch 
                # create the files
            $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs tar czvf archive.tgz
                # will invoke tar several time as it can'f fit 10000 long filenames into 1
            $ tar tzvf archive.tgz | wc -l
            60
                # in my own machine, I end up with only the 60 last filenames, 
                # as the last invocation of tar by xargs overwrote the previous one(s)
            
            # proper way to invoke tar: with -r  (which append to an existing tar file, whereas c would overwrite it)
            # caveat: you can't have it compressed (you can't add to a compressed archive)
            $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs tar rvf archive.tar #-r, and without z
            $ gzip archive.tar
            $ tar tzvf archive.tar.gz | wc -l
            10000 
              # we have all our files, despite xargs making several invocations of the tar command
            
             
            

            注意:xargs 的行为是众所周知的错误,这也是为什么,当有人想做的时候:

            find .... | xargs grep "regex"
            

            他们必须写出来:

            find ..... | xargs grep "regex" /dev/null
            

            这样,即使 xargs 对 grep 的最后一次调用只附加了 1 个文件名,grep 也会看到至少 2 个文件名(每次都有:/dev/null,它不会找到任何东西,并附加了 the filename(s)通过 xargs 之后),因此当某些东西匹配“正则表达式”时将始终显示文件名。否则,您最终可能会看到最后的结果显示前面没有文件名的匹配项。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-19
              • 1970-01-01
              相关资源
              最近更新 更多