如何修复 stderr 与 stdout 混合的 tar 文件?

如何修复 stderr 与 stdout 混合的 tar 文件?

我有一个正在尝试修复的 tar 文件。问题是tar's stderr 与其混合在一起stdout,因此文件如下所示:

% 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/

我尝试删除错误消息,如下所示:

% 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

但正如您所看到的,这会产生错误。 (我不会给你带来可怕的二进制数据文件转储,除非它是真的需要。)

我还应该指出,这个文件非常大(6.7 GB),而且我没有那么多可用的核心或交换。

tar该 tar 包正在使用用于创建它的 GNU Tar 1.27.1进行解压。

根据@kos的建议,我尝试使用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

为了您的方便,这里有一个重现问题的脚本:

#!/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"

尽管在我的 tarball 中,错误混合在中间,但我似乎无法在这里重现。

答案1

只要 和stdoutstderr行缓冲的,并且两者之一的行始终是可发现的,那么混合它们就不是问题:考虑程序的输出,其中stdoutstderr都是行缓冲的,并且其中stderr很容易可发现:

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

使用以下命令提取两者中的任何一个grep都不是问题:

$ < 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

stdout但是,请考虑在某些时候的缓冲行为更改为无缓冲的情况:

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

提取stdout使用grep 一个问题:

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

试试这个:

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

除[边缘]情况外,应从stdout文件中提取原始内容:

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

相关内容