撤消tar文件提取混乱


34

我只是解压缩了一个归档文件,该归档文件将杂乱的文件生成到我的整洁目录中。例如:

user@comp:~/tidy$ tar xvf myarchive.tar
file1
file2
dir1/
dir1/file1
dir1/subdir1/
dir1/subdir1/file1
dir2/
dir2/file1
...

我期望tar文件可以被组织在一个文件夹(即myarchive/)中,但事实并非如此!现在,我已经有190个文件和目录以数字方式被发送到一个有组织的目录中。这些未压缩的文件需要清理。

有什么方法可以“撤消”此操作并删除从此存档中提取的文件和目录?


感谢您下面的出色回答。总而言之,以下是两个步骤的工作:(1)删除文件,(2)以相反的打包顺序删除空目录结构(首先删除外部目录):

tar tf myarchive.tar | xargs -d'\n' rm
tar tf myarchive.tar | tac | xargs -d'\n' rmdir

而且更加安全,可以通过echo在之后附加来预览命令的预览版xargs


我猜您可以列出存档中的文件并将其从当前目录中删除,但是这可能会破坏数据(要保留的数据)。我也不知道如何编写bash脚本,所以我无济于事。
鲍勃

幸运的是,没有任何东西被覆盖!
Mike T

我不在意销售代表,而且无论我怎么说,我都会听起来很古怪,我也不喜欢(我也喜欢slhck的回答,并且我+1:编辑它,说实话:±15个代表是而不是我的世界),但是您最终还是将我建议的答案与管道和xargstac而不sort -r只是化妆品)一起使用,但是您接受了流程替代的答案,正如您在评论中所述,该方法不适合您?另外,xargs -d'\n'如果您想为将来的用户进行总结,请在您的帖子中进行切换,这样他们就不会被文件名中的空格所困扰。
丹尼尔·安德森

@DanielAndersson,直到现在我一直都不了解它的必要性-d'\n',并且经过进一步分析,您的答案实际上更接近于我所使用的答案。
Mike T

对此也完全没问题,喜欢@Daniel的解决方案:)的必要性-d'\n'在于以下事实:如果您不告诉您xargs在新行(这是您要提供的内容)上而是在空格处拆分参数,则使用名称folder1/some file将读取为folder1/somename
slhck

Answers:


36
tar tf archive.tar

将逐行列出内容。

这可以xargs直接通过管道传递给您,但请注意:删除时要非常小心。您并不想只是告诉您rm -r所有内容tar tf,因为它可能包含在解压缩之前不为空的目录!

你可以做

tar tf archive.tar | xargs -d'\n' rm -v
tar tf archive.tar | sort -r | xargs -d'\n' rmdir -v

首先删除存档中的所有文件,然后删除保留为空的目录。

sort -r(glennjackman建议tac而不是sort -r在接受的答案的注释中使用该方法,因为tar的输出足够有规律,因此也可以使用)首先需要删除最深的目录;否则,在传递之后将保留dir1一个包含空目录的情况,因为在删除之前该目录不为空。dir2dir1rmdirdir2

这会产生很多

rm: cannot remove `dir/': Is a directory

rmdir: failed to remove `dir/': Directory not empty
rmdir: failed to remove `file': Not a directory

2>/dev/null如果让您感到烦恼,请关闭它,但我希望在此过程中保留尽可能多的信息。

而且,除非您确定匹配正确的文件,否则请不要这样做。也许尝试rm -i确认所有内容。并备份,吃早餐,刷牙等。


是的,最好将-d'\n'选项传递给xargs
斯特凡吉梅内斯

@slhck和Stéphane:嗯,是的,我会更新。我只是做了一个小测试用例,但是文件没有空格。
Daniel Andersson

1
应该注意的是BSD xargs没有-d,所以如果您像我这样贫穷的人,则需要GNU变体。
slhck

10

像这样列出tar文件的内容:

tar tzf myarchive.tar

然后,通过遍历该列表来删除这些文件名:

while IFS= read -r file; do echo "$file"; done < <(tar tzf myarchive.tar.gz)

这仍将仅列出将要删除的文件。更换echorm,如果你真的肯定这是要删除的人。也许要确定要备份。

在第二遍中,删除剩下的目录:

while IFS= read -r file; do rmdir "$file"; done < <(tar tzf myarchive.tar.gz)

如果目录已经存在,这可以防止将其删除。


@glennjackman的另一个不错的技巧,即从最深的文件开始,保留文件的顺序。同样,echo完成后将其删除。

tar tvf myarchive.tar | tac | xargs -d'\n' echo rm

然后可以进行常规rmdir清理。


写管道的奇怪方法。
斯特凡吉梅内斯

不是管道。它是过程替换,与while循环遍历一组记录结合使用时,我更喜欢这种方法而不是简单的管道。刚习惯。@STÉ
slhck

1
抱歉,延迟很小,我注意到使用rm -rf可以删除不是从档案中而是位于与档案中同名的目录中的文件。最好在这里小心,并rmdir在第二遍使用。
斯特凡吉梅内斯

1
实际上,rmdir对于目录嵌套的每个级别,都需要运行with的第二遍。因此它将subdir1在第一遍清除,但dir1由于它在当时不为空时试图先删除它而离开,因此退出。如果文件列表可以反向排序,则此命令可以执行一次。
Mike T

3
如果您想以相反的顺序tar tvf arch.tar | tac | xargs echo rm删除:(确信时删除回声)
格伦·杰克曼

2

这可能会提取提取的文件并将其移动到子目录,从而清理您的主文件夹。

    #!/usr/bin/perl -w

    use strict;
    use Getopt::Long;

    my $clean_folder = "clean";
    my $DRY_RUN;
    die "Usage: $0 [--dry] [--clean=dir-name]\n"
        if ( !GetOptions("dry!" => \$DRY_RUN,
                         "clean=s" => \$clean_folder));

    # Protect the 'clean_folder' string from shell substitution
    $clean_folder =~ s/'/'\\''/g;

    # Process the "tar tv" listing and output a shell script.
    print "#!/bin/sh\n" if ( !$DRY_RUN );
    while (<>)
    {
        chomp;

        # Strip out permissions string and the directory entry from the 'tar' list
        my $perms = substr($_, 0, 10);
        my $dirent = substr($_, 48);

        # Drop entries that are in subdirectories
        next if ( $dirent =~ m:/.: );

        # If we're in "dry run" mode, just list the permissions and the directory
        # entries.
        #
        if ( $DRY_RUN )
        {
            print "$perms|$dirent\n";
            next;
        }

        # Emit the shell code to clean up the folder
        $dirent =~ s/'/'\\''/g;
        print "mv -i '$dirent' '$clean_folder'/.\n";
    }

将其保存到文件中fix-tar.pl,然后像这样执行:

$ tar tvf myarchive.tar | perl fix-tar.pl --dry

这将确认您的tar列表像我的一样。您应该得到如下输出:

-rw-rw-r--|batch
-rw-rw-r--|book-report.png
-rwx------|CaseReports.png
-rw-rw-r--|caseTree.png
-rw-rw-r--|tree.png
drwxrwxr-x|sample/

如果看起来不错,请像这样再次运行:

$ mkdir cleanup
$ tar tvf myarchive.tar | perl fix-tar.pl --clean=cleanup > fixup.sh

fixup.sh脚本将是将顶层文件和目录移动到“干净”文件夹(在这种情况下,称为的文件夹cleanup)的shell命令。窥视一下此脚本,以确认它们完全符合犹太标准。如果是这样,您现在可以使用以下方法清理混乱:

$ sh fixup.sh

我更喜欢这种清除方式,因为它不会销毁尚未被该初始值覆盖的任何内容tar xv

注意:如果最初的空运行输出看起来不正确,那么您应该能够在两个substr函数调用中摆弄数字,直到看起来正确为止。该$perms变量仅用于空运行,因此实际上只有$dirent子字符串才需要正确。

另一件事:如果列表中的用户名和/或组名使名称以不可预测的列开头,则可能需要使用该tar选项。--numeric-ownertar


1

这种(反种族的)档案库由于其功能而被称为焦油炸弹。一旦这些“爆炸”事件之一出现在您身上,其他答案中的解决方案就会比我建议的更好。

但是,最好的“解决方案”是首先防止出现此问题。

最简单(最懒惰)的方法是始终将tar存档解压缩到一个空目录中。如果它包含顶层目录,则只需将其移至所需的目的地。如果不是,则只需重命名您的工作目录(空目录)并将其移动到所需位置。

如果您只是想第一次使用它,则可以运行tar -tvf archive-file.tar | 较少,它将列出存档的内容,因此您可以查看其结构,然后执行必要的操作以将其提取到所需的位置。

如果您想检查档案的内容以查看其内容是否在其中,可以使用t选项。如果可以,您可以选择提取所需的文件。

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.