Отменить беспорядок извлечения файла 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, поэтому не могу помочь.
Боб
К счастью, ничего не было перезаписано!
Майк Т
Я не после повторения, и я боюсь, что я буду звучать капризно, независимо от того, как я это излагаю, что мне не нравится (мне тоже понравился ответ slhck, и я +1: отредактировал его, и честно: ± 15 повторений не мой мир), но вы в конечном итоге используете мой предложенный ответ с трубками и xargs( tacа sort -rне просто косметику), но вы принимаете ответ с заменой процесса, который, как вы объяснили в комментариях, вам не подходит? Кроме того, укажите xargs -d'\n'в своем сообщении, если вы хотите подвести итоги для будущих пользователей, чтобы они не были укушены пробелами в именах файлов.
Даниэль Андерссон
@DanielAndersson, я никогда не понимал необходимость -d'\n'до сих пор, и после дальнейшего анализа ваш ответ на самом деле ближе к тому, что я использовал.
Майк Т
Полностью с этим тоже понравилось, решение @ Daniel понравилось :) Необходимость -d'\n'заключается в том факте, что если вы не скажете xargsразделять аргументы по новым строкам (что вы и вводите), а по пробелам, то файл с имя folder1/some fileбудет читаться как folder1/someи name.
Slhck

Ответы:

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содержится один пустой каталог, dir2будет удален dir1после rmdirпередачи, поскольку он не был пустым до dir2удаления.

Это создаст много

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 и Стефан: Ах да, я обновлю. Я только что сделал небольшой тестовый пример, но в файлах не было пробелов.
Даниэль Андерссон
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)

Это по-прежнему будет просто список файлов, которые будут удалены. Замените echoна, rmесли вы действительно уверены, что это те, которые вы хотите удалить. И, возможно, сделать резервную копию, чтобы быть уверенным.

Во втором проходе удалите оставшиеся каталоги:

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очистка.

slhck
источник
Странный способ написать трубу.
Стефан Гименес
Это не труба. Это подстановка процесса, и я предпочитаю это, а не простой конвейер, когда используется в сочетании с whileциклическим перебором набора записей. Просто привык к этому. @ sté
slhck
1
Извините за небольшую задержку, я заметил, что использование rm -rfможет удалить файлы, которые были не из архива, а внутри каталога, имя которого совпадает с именем из архива. Лучше быть осторожным здесь и использовать rmdirво втором проходе.
Стефан Гименес
1
На самом деле второй проход с rmdirнеобходимо запустить для каждого уровня вложенности каталогов. Таким образом, он будет очищен subdir1при первом проходе, но уйдет, dir1поскольку он попытался удалить его первым, когда он не был пустым в то время. Эту команду можно выполнить один раз, если список файлов можно отсортировать в обратном порядке.
Майк Т
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). Посмотрите этот скрипт, чтобы убедиться, что он кошерный. Если это так, теперь вы можете навести порядок:

$ sh fixup.sh

Я предпочитаю такую ​​очистку, потому что она не уничтожает ничего, что еще не было уничтожено перезаписью этой начальной буквой tar xv.

Примечание: если этот исходный результат пробного запуска выглядит неправильно, вы должны иметь возможность поиграть с числами в двух substrвызовах функций, пока они не будут выглядеть правильно. $permsПеременная используется только для сухого хода так действительно только $direntподстрок должно быть правильным.

Еще одна вещь: вам может понадобиться использовать эту tarопцию, --numeric-ownerесли имена пользователей и / или имена групп в tarсписке делают имена начинающимися в непредсказуемом столбце.

S2VpdGgA
источник
1

Такого рода (антиобщественный) архив называют «бомбой-смолой» из-за того, что он делает. Как только один из них «взорвется» на вас, решения в других ответах будут намного лучше, чем я бы предложил.

Лучшее «решение», однако, состоит в том, чтобы предотвратить проблему в первую очередь.

Самый простой (самый ленивый) способ сделать это - всегда распаковывать архив tar в пустой каталог. Если он включает в себя каталог верхнего уровня, то вы просто перемещаете его в нужное место назначения. Если нет, просто переименуйте ваш рабочий каталог (тот, который был пустым) и переместите его в нужное место.

Если вы просто хотите сделать все правильно с первого раза, вы можете запустить tar -tvf archive-file.tar | меньше, и он будет перечислять содержимое архива, чтобы вы могли увидеть, как он структурирован, а затем сделать то, что необходимо, чтобы извлечь его в нужное место для начала.

Опция t также пригодится, если вы хотите проверить содержимое архива, просто чтобы посмотреть, есть ли в нем что-то, что вы ищете. Если это так, вы можете, при желании, просто извлечь файлы, которые вы хотите.

Джо
источник