查找文件并给它们加上沥青(使用空格)

好了,问题很简单。我在做一个简单的备份代码。它可以正常工作,除非文件中有空格。这就是我查找文件并将其添加到 tar 归档文件的方法:

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

问题在于当文件名中有一个空格时,因为 tar 认为它是一个文件夹。基本上有没有一种方法,我可以添加引号周围的结果从发现?还是用别的方法解决这个问题?

117804 次浏览

为什么不:

tar czvf backup.tar.gz *

Sure it's clever to use find and then xargs, but you're doing it the hard way.

更新: Porges 给出了一个查找选项的评论,我认为这个选项比我的答案或者另一个选项 find -print0 ... | xargs -0 ....要好

试试跑步:

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

用这个:

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

它会:

  • deal with files with spaces, newlines, leading dashes, and other funniness
  • 处理无限数量的文件
  • 不会重复覆盖 backup.tar.gz,就像使用 tar -cxargs在有大量文件时所做的那样

参见:

也许还有别的办法可以达到你的目的,基本上,

  1. Use the 找到 command to output path to whatever files you're looking for. Redirect Stdout to a filename of your choosing.
  2. 然后使用 -T 选项 tar,该选项允许它获取一个文件位置列表(就是您刚刚用 find 创建的那个文件位置!)

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

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

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

#!/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

如果您有多个文件或目录,并且希望将它们压缩到独立的 *.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

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

给你提供的另一种解决方案是:

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

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

对于那些通过无数次谷歌搜索找到这篇文章的人来说,我找到了一种方法,不仅可以找到给定时间范围内的特定文件,还可以避免包含可能导致沥青化错误的相对路径或空白。(非常感谢 STEVE)

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. 常规的 -print0-printf "%f"对我不起作用:

此引号的执行方式与 GNU ls 相同。这与用于-ls 和-fls 的引用机制不同。如果您能够决定对 find 的输出使用什么格式,那么通常最好使用’0’作为结束符,而不是使用换行符,因为文件名可能包含空格和换行符。

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

Edit 2019-08-14: 我想补充的是,我也可以在我的注释中使用同样的命令,只是使用 tar 本身:

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

Needed --ignore-failed-read in-case there were no new PDFs for today.

Big warning on several of the solutions (and your own test) :

当你这样做: 任何东西 | xargs 东西

Xargs 将尝试在“ something”之后放入“尽可能多的参数”,但是这样可能会导致对“ something”的多次调用。

因此,您的尝试是: find... | xargs tar czvf file.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

That way, even if the last invocation of grep by xargs appends only 1 filename, grep sees at least 2 filenames (as each time it has: /dev/null, where it won't find anything, and the filename(s) appended by xargs after it) and thus will always display the file names when something maches "regex". Otherwise you may end up with the last results showing matches without a filename in front.