我正在通过本地或网络位置的管道发送压缩文件。在接收端,我想检测压缩类型并使用适当的解压缩实用程序(gzip、bzip2、xz 等)来提取它。命令如下所示:
当地的:
cat misteryCompressedFile | [compressionUtility] -d -fc > /opt/files/uncompressedfile
通过网络:
ssh user@ipaddr "cat misteryCompressedFile" | [compressionUtility] -d -fc > /opt/files/uncompressedfile
即使没有提供扩展名(例如 .gz 或 .bz2),人们也可以通过查看文件的前几个十六进制值来判断所使用的压缩类型。例如,如果我用来xxd
查看两个压缩文件的前几个十六进制值,那么我将 1f8b 0808
查看 gzip 和425a 6836
bzip2。
但是,要仍然使用管道,如何检查第一个传入字节以便为文件的第一个文件选择正确的解压缩实用程序?
因此,如果未知压缩文件是 gzip 类型,命令将是这样的:
cat misteryCompressedFile | gzip -d -fc > /opt/files/uncompressedfile
如果未知压缩文件是 bzip2 类型,命令将是这样的:
cat misteryCompressedFile | bzip2 -d -fc > /opt/files/uncompressedfile
是否可以通过管道动态地做出这样的决定,而不需要下载整个文件,然后再决定使用什么进行解压缩?
答案1
是的,您可以在管道中执行此操作,而无需读取整个文件。
第一个脚本片段说明了我们拦截和检查标头并将其传递的机制。请注意,我们将标头打印到 stderr (>&2),但它仍然出现在输出中:
$ echo 0123456789ABCDEF |
(
HEADER=$(dd bs=1 count=4);
printf 'HEADER:%s\n' "$HEADER" >&2;
printf '%s\n' "$HEADER";
cat
)
4+0 records in
4+0 records out
4 bytes (4 B) copied, 8.4293e-05 s, 47.5 kB/s
HEADER:0123
0123456789ABCDEF
$
关键是使用dd
小块大小的文件转换实用程序bs=1
。
扩展一下,这是一个可行的解决方案。我们将使用临时文件来存储二进制头。如果它没有看到两个 4 字节标头之一,那么它什么也不做:
#!/bin/sh
trap "rm -f /tmp/$$; exit 1" 1 2 3 15
# grab the 1st 4 bytes off the input stream,
# store them in a file, convert to ascii,
# and store in variable:
HEADER=$(
dd bs=1 count=4 2>/dev/null |
tee /tmp/$$ |
od -t x1 |
sed '
s/^00* //
s/ //g
q
'
)
case "$HEADER" in
1f8b0800)
UNCOMPRESS='gzip -d -fc'
;;
425a6839)
UNCOMPRESS='bzip2 -d -fc'
;;
*)
echo >&2 "$0: unknown stream type for header '$HEADER'"
exit 2
;;
esac
echo >&2 "$0: File header is '$HEADER' using '$UNCOMPRESS' on stream."
cat /tmp/$$ - | $UNCOMPRESS
rm /tmp/$$
答案2
在发送计算机上使用file
并使用该信息来决定在远程主机上运行哪个解压命令。
例如
#! /bin/sh
filetype=$(file misteryCompressedFile)
case "$filetype" in
*gzip*) CMD='gzip' ; ARGS='-d -fc' ;;
*bzip2*) CMD='bzip2' ; ARGS='-d -fc' ;;
*) echo "error: unknown compression type" ; exit 1 ;;
esac
cat misteryCompressedFile | ssh user@ipaddr "$CMD $ARGS > /opt/files/uncompressedfile"
在所示的示例中,gzip
和bzip2
命令的 ARGS 是相同的...但对于其他解压工具来说它们可能不同。
这是一个解压缩从远程主机获取的文件的版本:
#! /bin/sh
# set up an anonymous fifo on fd 3 so we can pass the
# output of `file` to the second subshell without risking
# corruption of stdout/stdin
FIFO=$(mktemp -u)
mkfifo "$FIFO"
exec 3<>"$FIFO"
rm -f "$FIFO"
ssh user@ipaddr 'cat misteryCompressedFile' |
(
HEADER=$(dd bs=1 count=20 2> /dev/null |
od -A none -t o1 -w512 |
sed -e 's: :\\:g')
printf "$HEADER" | file --mimetype - | cut -d/ -f2 >&3
printf "$HEADER"
cat
) | (
read -u 3 -r filetype
case "$filetype" in
gzip) CMD='gzip' ; ARGS='-d -fc' ;;
x-bzip2) CMD='bzip2' ; ARGS='-d -fc' ;;
x-xz) CMD='unxz' ; ARGS='' ;;
x-lzma) CMD='lzcat' ; ARGS='' ;;
x-compress) CMD='uncompress' ; ARGS='' ;;
x-lrzip) CMD='lrzcat' ; ARGS='' ;;
*) echo "error: unknown compression type" >&2 ; exit 1 ;;
esac
$CMD $ARGS > /opt/files/uncompressedfile
)