tar --exclude不排除。为什么?


71

我在bash脚本中有这条非常简单的行,该脚本可以成功执行(即生成_data.tar文件),除了它排除通过--exclude选项告知其被排除的子目录外:

/bin/tar -cf /home/_data.tar  --exclude='/data/sub1/*'  --exclude='/data/sub2/*' --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'  /data

相反,它将生成一个_data.tar文件,其中包含/ data下的所有内容,包括我要排除的子目录中的文件。

知道为什么吗?以及如何解决这个问题?

更新我根据下面第一个答案中提供的链接实现了我的观察(首先是顶层目录,最后一个排除后没有空格):

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1/*'  --exclude='/data/sub2/*'  --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'

但这没有帮助。所有“排除的”子目录都存在于结果_data.tar文件中。

这令人困惑。无论这是当前tar中的错误(CentOS 6.2,Linux 2.6.32上的GNU tar 1.23)还是tar对空白和其他易于忽略的错别字的“极端敏感性”,我都认为这是一个错误。目前。

这太可怕了:我尝试了以下建议的见解(无尾随/*),但在生产脚本中仍然无效:

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1'  --exclude='/data/sub2'  --exclude='/data/sub3'  --exclude='/data/sub4'

除了引号和2个空格(而不是1),我看不到尝试和@Richard Perrin尝试的内容之间的任何区别。我将尝试这种方式(必须等待每晚运行的脚本作为要备份的目录)数量庞大)并进行报告。

/bin/tar -cf /home/_data.tar  /data --exclude=/data/sub1 --exclude=/data/sub2 --exclude=/data/sub3 --exclude=/data/sub4

我开始认为所有这些tar --exclude敏感性不是焦油的而是环境中的某种东西,但是那又会是什么呢?

有效!最后一个变体尝试了工作(在--excludes 之间没有单引号和单空格,而不是双空格)。很奇怪,但是接受。

难以置信的!事实证明,tar仅当顶级目录位于命令行的最后时,才会排除(1.15.1)的较旧版本。这与1.23版的要求完全相反。仅供参考。

Answers:


50

如果要排除整个目录,则模式应与该目录匹配,而不是其中的文件。使用--exclude=/data/sub1代替--exclude='/data/sub1/*'

引用这些模式时要小心,以防止它们受到外壳扩展的影响。

参见以下示例,最终调用遇到麻烦:

$ for i in 0 1 2; do mkdir -p /tmp/data/sub$i; echo foo > /tmp/data/sub$i/foo; done
$ find /tmp/data
/tmp/data
/tmp/data/sub2
/tmp/data/sub2/foo
/tmp/data/sub0
/tmp/data/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude='/tmp/data/sub[1-2]'
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub2/
/tmp/data/sub2/foo
/tmp/data/sub0/
/tmp/data/sub0/foo
/tmp/data/sub2/
tar: Removing leading `/' from hard link targets
/tmp/data/sub2/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub1 /tmp/data/sub2

感谢您提出的重点明确的答案。关于您的第一点,我正在尝试遵循此LQ线程中的提示。我不确定我错过了什么,但是现在我阅读了您的第二点,很可能是绝对路径还是相对路径问题。我将尝试并报告。现在+1。
ateiob 2012年

我注意到的另一件事是--exclude b(空格而不是等号)vs --exclude=b。这有什么区别吗?(不应恕我直言)
ateiob 2012年

1
等号对于避免未引用模式的外壳扩展可能至关重要。如果您有空格,那么外壳可以将未加引号的模式扩展为单个--exclude参数,而其余的扩展将作为文件添加到tar文件中。首先,您的示例带有“ =”-如果脚本没有,并且缺少单引号,则可能是问题的根源。
R Perrin '02年

好。我在盒子上测试了您的示例,即使--exclude=在同一行中有多个示例,它也可以正常工作。所以区别必须是/*我附加到每个子目录的白痴。我今晚将在生产脚本中对此进行测试并进行报告。另一个+1。
ateiob 2012年

对我来说,@ carlo的答案是一个特定的问题-愚蠢的tar不能将--exclude作为命令行上的最后一个选项-显然引起很多麻烦。谢谢大家
moodboom

32

可能是您的的版本tar要求必须将--exclude选项放在tar命令的开头。

请参阅:https//stackoverflow.com/q/984204

tar --exclude='./folder' --exclude='./upload/folder2' \
    -zcvf /backup/filename.tgz .

请参阅:http : //mandrivausers.org/index.php?/topic/8585-multiple-exclude-in-tar/

tar --exclude=<first> --exclude=<second> -cjf backupfile.bz2 /home/*

选择:

EXCLD='first second third'
tar -X <(for i in ${EXCLD}; do echo $i; done) -cjf backupfile.bz2 /home/*

另一个tar命令提示是从这里

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject

请参阅上面的更新。尝试的最后一个变体(无引号,单个空格)有效。我不知道为什么。+1是经过深思熟虑的答案+链接。
ateiob 2012年

仅供参考,在debian下,如果我不精确过滤器,--exclude=mydir/*那么它将无法正常工作(使用tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*)。
奥利维尔·庞斯

1
@OlivierPons而不是“在debian下”,或者用它来放置tar(tar --version)的版本;多年来,debian可能会附带许多不同版本的tar。
msouth

1
我的版本(1.29)仅适用于--excludebefore -czf
falsePockets

8

要排除多个文件,请尝试

--exclude=/data/{sub1,sub2,sub3,sub4}

这将节省一些代码和麻烦。这是针对所有程序/选项的全局解决方案。如果您还希望在选择中包括上级目录(在这种情况下为数据),则必须包括尾部逗号。例如:

umount /data/{sub1,sub2,}

3
我爱冰壶。我发现即使有多年的Unix经验,很多人也不了解它们。 mv /very/very/very/very/long/path/to/a/file{,.bak}
msouth

5

该链接可能会有所帮助。 http://answers.google.com/answers/threadview/id/739467.html

非工作行与链接中的一些提示之间有两个直接区别:

  1. 所有来排除的顶级目录。
  2. 最后一个之后不能有任何空格--exclude

谢谢。通过回答-MAK引起了我的注意,到目前为止,我已经能够发现我的非工作线及以下的以下区别:1,所有来排除的顶级目录。2. last之后不能有任何空格--exclude。我将测试这些见解并进行报告。现在+1。
ateiob 2012年

@ateiob如果您能解决问题,可以在这里发布答案还是编辑该答案?我们一般不希望没有其他链接提供的答案
Michael Mrozek

@Michael Mrozek绝对。这正是我在评论中写的。:)
ateiob 2012年

3

解决方法可能是使用find ... -prunetar排除指定的目录。

在Mac OS X上--exclude,GNU 的选项tar似乎可以正常工作。

在以下测试案例中,目录/private/var/log/asl/private/var/log/DiagnosticMessages将从/private/var/log目录的压缩存档中排除。

# all successfully tested in Bash shell on Mac OS X (using gnutar and gfind)

# sudo port install findutils  # for gfind from MacPorts

sudo gnutar -czf ~/Desktop/varlog.tar.gz /private/var/log --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages"

sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages" /private/var/log

set -f # disable file name globbing
sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/Diagnostic*" /private/var/log

# combining GNU find and tar (on Mac OS X)

sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "DiagnosticMessages" \) -prune -o -print0 | 
   sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -

# exclude even more dirs
sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "[Dacfks]*" \) -prune -o -print0 | 
    sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -


# testing the compressed archive

gnutar -C ~/Desktop -xzf ~/Desktop/varlog.tar.gz

sudo gfind /private/var/log ~/Desktop/private \( -iname DiagnosticMessages -or -iname asl \)

sudo rm -rf ~/Desktop/varlog.tar.gz ~/Desktop/private

感谢+1的建议。在这一点上,我仍在试图理解为什么cron每晚都会在我的脚本中没有完善的(成熟的)功能。
ateiob 2012年

3

也许您可以尝试使用带有其他选项的命令:

--wildcards

并检查它是否按预期运行。


请参阅上面的更新。尝试的最后一个变体(无引号,单个空格)有效。我不知道为什么。+1这个想法。
ateiob 2012年

3

我正在使用Mac,发现除非顶层文件夹是最后一个参数,否则排除项将无法工作

工作命令示例:

tar czvf tar.tgz --exclude='Music' dir

仅供参考:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3

通过Ubuntu 14.04的tar 1.27.1也是如此。
格雷格·贝尔

3

就我而言,它并没有因为其他原因而被排除在外。

完整路径与相对路径。

排除和目录都必须使用相同的路径格式(即,全部路径或全部相对路径)

例:

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' ctms-db-sync

这将不起作用,因为排除使用完整路径,而目标使用相对路径

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' /home/mine/tmp/ctms-db-sync

之所以可行,是因为两者都使用完整路径

tar -cvf ctms-db-sync.tar --exclude='ctms-db-sync/sql' ctms-db-sync

之所以可行,是因为两者都使用相对路径



1

R Perrin出色答案的其他说明:

假设您不想归档绝对路径,而是相对路径,例如,“数据”而不是“ / tmp / data”。要排除绝对路径,您使用的tar参数(取决于tar实现(gnu tar与bsd tar))会有所不同:

$ for i in 0 1 2; do
    for j in 0 1 2; do 
      mkdir -p /tmp/data/sub$i/sub$j
      echo foo > /tmp/data/sub$i/sub$j/foo
    done
  done

$ find /tmp/data/
/tmp/data/
/tmp/data/sub2
/tmp/data/sub2/sub2
/tmp/data/sub2/sub2/foo
/tmp/data/sub2/sub1
/tmp/data/sub2/sub1/foo
/tmp/data/sub2/sub0
/tmp/data/sub2/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/sub2
/tmp/data/sub1/sub2/foo
/tmp/data/sub1/sub1
/tmp/data/sub1/sub1/foo
/tmp/data/sub1/sub0
/tmp/data/sub1/sub0/foo
/tmp/data/sub0
/tmp/data/sub0/sub2
/tmp/data/sub0/sub2/foo
/tmp/data/sub0/sub1
/tmp/data/sub0/sub1/foo
/tmp/data/sub0/sub0
/tmp/data/sub0/sub0/foo

$ cd /tmp/data; tar -zvcf /tmp/_data.tar --exclude './sub[1-2]'
./
./sub0/
./sub0/sub2/
./sub0/sub2/foo
./sub0/sub1/
./sub0/sub1/foo
./sub0/sub0/
./sub0/sub0/foo

# ATTENTION: bsdtar's behaviour differs from traditional tar (without a leading '^')!
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude './sub[1-2]' .
a .
a ./sub0
a ./sub0/sub0
a ./sub0/sub0/foo

# FIX: Use a regex by adding a leading '^' will cause bsdtar to match only parent files and folders.
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
# ALTERNATIVE: bsdtar -C /tmp/data -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
a .
a ./sub0
a ./sub0/sub2
a ./sub0/sub1
a ./sub0/sub0
a ./sub0/sub0/foo
a ./sub0/sub1/foo
a ./sub0/sub2/foo

1

刚刚在tar(GNU tar)1.29上检测到

此调用不会从--exclude-from指定的存档文件中排除:

/bin/tar --files-from ${datafile} --exclude-from ${excludefile} -jcf ${backupfile}

此调用可以协同工作:

/bin/tar --exclude-from ${excludefile} --files-from ${datafile} -jcf ${backupfile}

参数的顺序很重要!


0

我尝试了各种组合,包括列出的一些答案,但无法排除列出的文件。

因此,对五分钟的工作一无所获,我就厌倦了:我做了相反的事情:创建了我包含的文件夹的存档。

我这样做是通过创建一个存档,然后添加到其中

tar -cvpf /path/to/mybackup.tar ./bin
tar rvf /path/to/mybackup.tar ./boot
tar rvf /path/to/mybackup.tar ./etc
tar rvf /path/to/mybackup.tar ./home
tar rvf /path/to/mybackup.tar ./lib
tar rvf /path/to/mybackup.tar ./sbin
tar rvf /path/to/mybackup.tar ./usr
tar rvf /path/to/mybackup.tar ./var

一些注意事项:

  • 通过从文件系统的根目录运行,我使用了相对路径而不是绝对路径(这也会给您带来麻烦)。
  • 您必须创建一个普通的tar(而不是压缩的tar .tgz/ .tar.gz)存档-您可以稍后使用zip将其压缩gzip mybackup.tar
  • 确保没有将归档文件放在要包含的任何文件夹中,否则您将获得一些递归(部分备份也包含在备份本身中)。
  • 注意第一个命令(创建)与其他命令(添加)的区别。
  • 如果您偏执,可以检查是否正在添加文件,而不是覆盖备份(例如在第二个命令之后)tar tvf mybackup.tar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.