Como posso reparar um arquivo tar em que o stderr foi misturado com stdout?

4

Eu tenho um arquivo tar que estou tentando reparar. O problema é que o stderr de tar foi misturado com seu stdout , então o arquivo se parece com isto:

% head android-1435613730.tar
tar: removing leading '/' from member names
factory/0000775000175100017510000000000007033241671011512 5ustar  radioradiofactory/
factory/lost+found/0000700000000000000000000000000000000000000013242 5ustar  rootrootfactory/lost+found/
tar: /factory/lost+found: Permission denied
factory/wifi/0000770000175000017510000000000007033241625012667 5ustar  systemradiofactory/wifi/
tar: /factory/wifi: Permission denied
factory/imei/0000775000175100017510000000000007033241600012425 5ustar  radioradiofactory/imei/
tar: can't open '/factory/nv_data.bin': Permission denied
tar: can't open '/factory/nv_data.bin.md5': Permission denied
factory/bluetooth/0000755000175100017510000000000007033241674013520 5ustar  radioradiofactory/bluetooth/

Eu tentei remover as mensagens de erro da seguinte forma:

% grep --color=never -v --binary-file=text '^tar:.*$' android-1435613730.tar | tar -tv
drwxrwxr-x radio/radio       0 1999-12-31 16:00 factory/
tar: Skipping to next header
drwx------ install/all_a124  0 2015-06-29 13:51 acct/uid/50124/
tar: Skipping to next header
tar: Exiting with failure status due to previous errors

Mas, como você pode ver, isso gera um erro. (Vou poupá-lo do terrível despejo de arquivo de dados binários, a menos que seja realmente necessário.)

Eu também devo salientar que este arquivo é bem grande (6,7 GB), e eu não tenho tanto núcleo ou swap disponível.

O tarball está sendo descompactado com o mesmo tar que foi usado para criá-lo, GNU Tar 1.27.1.

Por sugestão do @kos, eu tentei usar o Perl:

% <android-1435613730.tar perl -pe 's/\n?tar: [^\n]*\n//sg' | tar -tv
drwxrwxr-x radio/radio       0 1999-12-31 16:00 factory/
tar: Skipping to next header
drwx------ install/all_a83   0 2015-06-29 13:55 acct/uid/50083/
tar: Skipping to next header
tar: Exiting with failure status due to previous errors

Para sua conveniência, aqui está um script para recriar o problema:

#!/bin/sh
TMPDIR=$(mktemp -d)
cd $TMPDIR
for i in test test2 test3; do
    mkdir $i
    echo $i > $i/$i
done
chmod 000 test2/test2
chmod 000 test3
tar -c test* > broken.tar 2>&1

echo "Created corrupt tarball in $TMPDIR"

Embora no meu tarball os erros estejam misturados no meio, o que eu não consegui reproduzir aqui.

    
por strugee 01.07.2015 / 02:46

1 resposta

3

Contanto que stdout e stderr tenham buffer de linha e as linhas de um dos dois sejam sempre spottable, misturá-las não é um problema: considere a saída de um programa em que stdout e stderr são preenchidos com linha e em que stderr é facilmente reproduzível:

$ cat file
xxxxxxxxxx
tar: ----------
yyyyyyyyyy
tar: ----------
zzzzzzzzzz
tar: ----------

A extração de um dos dois usando grep não é um problema:

$ < file grep -v ^tar
xxxxxxxxxx # stdout line 1
yyyyyyyyyy # stdout line 2
zzzzzzzzzz # stdout line 3
$ < file grep ^tar
tar: ---------- # stderr line 1
tar: ---------- # stderr line 2
tar: ---------- # stderr line 3

No entanto, considere o caso em que, em algum momento, o comportamento do buffer stdout muda para, digamos, sem buffer:

x
tar: ----------
xxxxxxxxxyyy
tar: ----------
yyyyyyyzzzzz
tar: ----------
zzzzz

A extração de stdout usando grep é um problema:

$ < file grep -v ^tar
x # wrong stdout line 1
xxxxxxxxxyyy # wrong stdout line 2
yyyyyyyzzzzz # wrong stdout line 3
zzzzz # wrong stdout line 4

Tente isso:

< file perl -0777pe 's/\n?tar: [^\n]*\n//g' > newfile

Qual, excluindo os casos de [borda], deve extrair o stdout original do arquivo:

$ < file perl -0777pe 's/\n?tar: [^\n]*\n//g'
xxxxxxxxxxyyyyyyyyyyzzzzzzzzzz
    
por 01.07.2015 / 04:14