Как многократно распаковывать tar-файлы, которые находятся внутри самого tar

[[... ]]токенизация конфликтует с регулярными выражениями (подробнее об этом в мой ответ на ваш следующий -вопрос вверх\перегружен как оператор кавычек оболочки и оператор регулярного выражения (с некоторые помехи между ними в bash ), и даже когда нет очевидной причины для конфликта, поведение может быть неожиданным. Правила могут сбивать с толку.

Кто может сказать, что они будут делать, не попробовав (на всех возможных входных данных )с любой данной версией bash?

[[ $a = a|b ]]
[[ $a =~ a|b ]]
[[ $a =~ a&b ]]
[[ $a =~ (a|b) ]]
[[ $a =~ ([)}]*) ]]
[[ $a =~ [/\(] ]]
[[ $a =~ \s+ ]]
[[ $a =~ ( ) ]]
[[ $a =~ [ ] ]]
[[ $a =~ ([ ]) ]]

Вы не можете заключать регулярные выражения в кавычки, потому что если вы это сделаете, начиная с bash 3.2 и если совместимость с bash 3.1 не включена, цитирование регулярных выражений удаляет особое значение оператора RE. Например,

[[ $a =~ 'a|b' ]]

Соответствует, если $aсодержит только литерал a|b.

Сохранение регулярного выражения в переменной позволяет избежать всех этих проблем, а также делает код совместимым с ksh93иzsh(при условии, что вы ограничиваетесь POSIX ERE):

regexp='a|b'
[[ $a =~ $regexp ]] # $regexp should *not* be quoted.

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

2
07.10.2019, 17:41
2 ответа

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

file=(*tar); while [[ -e $file ]]; do tar xf "$file"; rm "$file"; file=(*tar); done

Пояснение

  • file=(*tar);:установите переменную $fileтак, чтобы она содержала имя файла. Должен быть только один файл, соответствующий глобусу *tarв каталоге, в котором вы его запускаете.
  • while [[ -e $file ]]; do:пока $fileсуществует...
  • tar xf $file; rm "$file"; file=(*tar);:разархивируйте текущее значение $file, затем удалите только что извлеченный tar-архив и, наконец, установите переменную $fileна имя нового, пока единственного, tar-файла в каталоге.

А вот еще более прямой подход(спасибо @kusalananda!). Я сделал архив tarс помощью этих команд:

$ cat file 
This is the text!
$ file=file; for i in {1..1000}; do tar cf $i.tar $file; file=$i.tar; done

Итак, это файл с именем file, который содержит текст This is the text!\n. fileбыл смолен 1000 раз, и теперь у нас есть 1000.tar. Мы можем распечатать исходный текст с помощью:

$ awk -F'\0' '/[^\0]/{print $(NF)}' 1000.tar 
This is the text!

Обратите внимание, что на самом деле это не оригинальный текст. Исходная новая строка была съедена tar, но заменена на awk. Чтобы получить реальное значение из архива, вам нужно (последнее $вот моя подсказка, новой строки не было):

$ awk -F'\0' '/[^\0]/{printf "%s", $NF}' 1000.tar 
This is the text!$

В этом конкретном случае я также смог воссоздать имя файла, сказав awkнапечатать последнее поле ($NF),и 438-е поле перед последним:

awk -F'\0' '/[^\0]/{print $(NF) >$(NF-438)}' 1000.tar 

Это создало новый fileс содержимым оригинала. То же самое, что извлечение. Однако я не знаю, является ли -428магическим числом. tar, по-видимому, добавляет в свой архив несколько NULL, поэтому я обнаружил это, запустив:

$ awk -F'\0' '{for(i=1;i<=NF;i++){ if($i ~ /file/){print i,NF-i}}}' 1000.tar 
434674 438

Это подсказало мне, что поле 434674 имеет содержимое file, а это 438 полей до последнего.

Если ваш awkподдерживает gsub, вы, вероятно, можете сделать его более общим с помощью:

awk -F'\0' '/[^\0]/{gsub(/\0+/,"\0"); print $NF > $(NF-11)}' 1000.tar 
3
27.01.2020, 21:50

Этот простой bashцикл for должен делать то, что вам нужно:

for i in {1000..1}; do tar -xf $i.tar; done
4
27.01.2020, 21:50

Теги

Похожие вопросы