2016-12-15 2 views
1

Я получил список tar-файлов (резервных копий) в каталоге и вам нужно развернуть их, от самых младших до самых старых до достижения успеха.Как распаковать файлы от младшего до старейшего до успеха в bash

Проблема заключается в том, что время от времени tar-файлы повреждаются и нуждаются в распаковке более старой версии.

вот как я нахожу младшего, но не уверен, как вставить это и найти следующий.

tar -xf `find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat | head -n 1` 

Заранее спасибо

ответ

1

У вас уже есть рабочий find | xargs ls, который дает вам отсортированный список файлов, сперва новые объявления. (Как указывает @sjsam, предполагается, что ни один из ваших имен файлов не содержит символы новой строки.) Вы можете использовать функцию bash, чтобы поместить этот список в файл, по одной записи в строке, так что это может быть re ad by a while ... read loop. Затем вы можете выйти из цикла, как только одна резервная копия завершится успешно.

# vvvvvvvvvvvvvvvvvvvvvv get the next filename into $tarname 
while IFS= read -r tarname 
do 
    tar -xf "$tarname" && break 
        # ^^^^^^^^ done with the loop if tar succeeds 
done < <(find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat) 
    #^ ^^^^---- the process substitution 
    # | 
    # +--------- the redirection from the process subsitution 

Обратите внимание, что вам нужно < <(...), а не только <(...).

(Кстати, вам нужно ls -hat, или же ls -t делать, а?)


Edit! ака Возьмите 3

Я был вдохновлен комментарий @ sjsam, чтобы написать ls0, замену ls, который может предоставить NULL завершающим выход. Проверьте это и сообщите о проблеме, если найдете какие-либо ошибки! Он был протестирован с Perl 5.22 на Cygwin, но должен работать для большинства Perl 5s. Это единственный файл — для установки, введите ls0 на свой путь и chmod ugo+x ls0.

Использование ls0, метод выше может обрабатывать странно названные файлы с апломбом:

while IFS= read -r -d '' tarname 
do 
    tar -xf "$tarname" && break 
done < <(find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | ls0 -t) 

(как отмечено @sjsam, -d '' функционально эквивалентна -d $'\0')


Take 2page sjsam links предлагает использовать perl вместо ls для сортировки имен файлов. Таким образом, вы можете обрабатывать имена файлов, в которых есть символы новой строки или другие странные символы.

Вот пример этой техники (не ответ на вопрос ОП). Я проверил его с именем файла, включая новую строку. Следующий код печатает файлы *.tar в текущем каталоге, новее сначала. Этот пример печатает косую черту до и после каждого имени файла, чтобы вы могли видеть, что имена файлов с символами новой строки обрабатываются правильно. Протестировано на cygwin, bash 4.3.46, perl 5.22.2.

perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \ 
| while IFS= read -r -d $'\0' name 
do 
    printf "/%s/\n" "$name" 
done 

Итак, положив, что все вместе для кода, который (1) отвечает на вопрос OP и (2) является устойчивым к имени файла с странного содержания:

perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \ 
| while read -r -d $'\0' tarname 
do 
    tar -xf "$tarname" && break 
done 

или, используя замену процесса,

while IFS= read -r -d $'\0' tarname 
do 
    tar -xf "$tarname" && break 
done < <(perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))') 

Легко, правда? ;)

Источников:

  • чтение каталоги в отсортированном порядке: this answer
    • reference для теста -M данные нулевого завершающего
  • чтения в Баше: this blog post (Баш-х read может используется с нулевыми символами)
  • f iltering списка в Perl: this answer
+0

[1] [http://mywiki.wooledge.org/ParsingLs#Why_you_shouldn.27t_parse_the_output_of_ls.281.29), чтобы проанализировать вывод ls. – sjsam

+1

@sjsam Достаточно честный! Проверьте редактирование :). – cxw

+0

Я использовал первую версию, так как в названии нет пробелов. – Alek

1

Ниже скрипт будет делать

find /path/to/tarball/folder -type f -name "*.tar" -printf "%[email protected]%p\0" | 
sort -rnz | while read -r -d '' stampedfile 
do 
    tar -xzf "${stampedfile#*--}" >/dev/null 2>&1 
    [ $? -eq 0 ] && echo "Newest Working Tar: ${stampedfile#*--}" && exit 
done 

Что мы сделали

  • Использования find перечислить нуль тарболл файлы, которые предваряются самым последнее доступное время в секундах с эпохи и --, которое мы используем в качестве разделителя позже
  • сортировать имена файлов в порядке убывания, используя время модификации. Это где -- ограничителя поставляется в удобной
  • разбор нуля отсортированных файлов, используя время цикл
  • раздеть time-- из имен файлов и пытаться распаковка его
  • Если попытка успешна, то выход
+1

Хороший материал, но почему неуклюжий тест '$?'? (Я предполагаю, что '$ 0' - это опечатка). Вы могли бы просто« && exit »непосредственно следовать за« tar »(или даже,' while read & & !tar; do echo failure> & 2; done') –

+0

Возможно, стоит также упомянуть, что это будет накапливать частичные результаты неудачных выводов tar, которые могут быть не такими, что нужно - в этом случае вам может потребоваться выполнить проход с сухим проходом с каждым файлом перед первым успешным. Однако неясно, что нужно. –

+0

@TobySpeight: Спасибо за указатель на '$?'. И да! несколько способов сокращения этого. Я объясняю длиннее.:) Cheerz – sjsam

Смежные вопросы