如何仅获取二进制文件的前十个字节

问题描述 投票:0回答:4

我正在编写一个 bash 脚本,需要获取文件的标头(前 10 个字节),然后在另一个部分中获取除前 10 个字节之外的所有内容。这些是二进制文件,前 10 个字节可能包含

\0
\n
。似乎大多数实用程序都使用 ASCII 文件。完成这项任务的好方法是什么?

bash binary
4个回答
194
投票

如前所述,要获取前 10 个字节:

head -c 10

获取除前 10 个字节之外的所有字节(至少对于 GNU

tail
):

tail -c+11

59
投票

head -c 10
在这里做了正确的事。


38
投票

您可以使用

dd
命令从二进制文件复制任意数量的字节。

dd if=infile of=outfile1 bs=10 count=1
dd if=infile of=outfile2 bs=10 skip=1

2
投票

如何在 bash 下分割文件

这里有两个答案!

阅读SO请求:

获取文件的标头(前 10 个字节),然后在另一个部分中获取除前 10 个字节之外的所有内容。

我明白了:

如何在特定点分割文件

因为这里的所有答案都会访问同一个文件两次,而不是仅仅分割!!

这是我的两分钱:

使用 Un*x 的有趣之处在于,将每个整个作业视为过滤器,使用无缓冲的I/O很容易分割。大多数标准 un*x 工具(

cat
grep
awk
sed
python
perl
...)都用作过滤器。

1.使用
head
dd
但单次使用

{ head -c 10 >head_part; cat >tail_part;} <file

这样效率更高,因为您的文件仅被读取 1 次,前 10 个字节转到

head_part
,其余的转到
tail_part

注意:第二个重定向

>tail_part
也可以放置在整个列表
{ ...;}
)之外...

您也可以使用
dd
:

做同样的事情
{ dd count=1 bs=10 of=head_part; cat;} <file >tail_part

这比运行两个

dd
进程来打开同一个文件两次更高效。

...并且仍然对文件的其余部分使用标准块大小

另一个基于按行读取的示例:

行附近拆分 HTTP(或邮件)流(仅包含 回车符的行:

\r
):

nc google.com 80 <<<$'GET / HTTP/1.0\r\nHost: google.com\r\n\r' |
    { sed -u '/^\r$/q' >/tmp/so_head.raw; cat;} >/tmp/so_body.raw

或者,删除空的最后一行:

nc google.com 80 <<<$'GET / HTTP/1.0\r\nHost: google.com\r\n\r' |
    { sed -nu '/^\r$/q;p' >/tmp/so_head.raw; cat;} >/tmp/so_body.raw

这将产生两个文件:

ls -l so_*.raw
-rw-r--r-- 1 root    root           307 Apr 25 11:40  so_head.raw
-rw-r--r-- 1 root    root           219 Apr 25 11:40  so_body.raw

grep www so_*.raw
so_body.raw:<A HREF="http://www.google.com/">here</A>.
so_head.raw:Location: http://www.google.com/

2.纯bash方式:

如果目标是在可用的 bash 变量中获取 ,这里有一个很好且有效的方法:

因为十个字节很少,可以避免fork到

head
。来自 在 BASH 中按字节读取文件:

read8() {
    local _r8_var=${1:-OUTBIN} _r8_car LANG=C IFS=
    read -r -d '' -n 1 _r8_car || { printf -v $_r8_var '';return 1;}
    printf -v $_r8_var %02X "'"$_r8_car
}
{ 
    first10=()
    for i in {0..9};do
        read8 first10[i] || break
    done
    cat
 } < "$infile" >"$outfile"

这将创建一个数组

${first10[@]}
,其中包含
$infile
前十个字节的十六进制值,并将其余数据存储到
$outfile

declare -p first10

declare -a first10=([0]="25" [1]="50" [2]="44" [3]="46" [4]="2D" [5]="31" [6]="2E"
[7]="34" [8]="0A" [9]="25")

这是一个 PDF(

%PDF
->
25 50 44 46
)...这是另一个示例:

{
    first10=()
    for i in {0..9};do
        read8 first10[i] || break
    done
    cat
} <<<"Hello world!"
d!

由于我没有重定向输出,字符串

d!
将在终端上输出。

echo ${first10[@]}
48 65 6C 6C 6F 20 77 6F 72 6C

printf '%b%b%b%b%b%b%b%b%b%b\n' ${first10[@]/#/\\x}
Hello worl

关于二进制

你说:

这些是二进制文件,前 10 个字节可能有

\0
\n

{
    first10=()
    for i in {0..9};do
        read8 first10[i] || break
    done
    cat
} < <(gzip <<<"Hello world!") >/dev/null 

echo ${first10[@]}
1F 8B 08 00 00 00 00 00 00 03

(底部带有

\n
的示例;)

作为函数

read8() { local _r8_var=${1:-OUTBIN} _r8_car LANG=C IFS=
    read -r -d '' -n 1 _r8_car || { printf -v $_r8_var '';return 1;}
    printf -v $_r8_var %02X "'"$_r8_car ;}
get10() {
    local -n result=${1:-first10}     # 1st arg is array name
    local -i _i
    result=()
    for ((_i=0;_i<${2:-10};_i++));do  # 2nd arg is number of bytes
        read8 result[_i] || { unset result[_i] ; return 1 ;}
    done
    cat
}

然后(这里,我使用特殊字符

表示:没有换行符。)。

get10 pdf 4 <$infile >$outfile
printf %b ${pdf[@]/#/\\x}
%PDF⛶

echo $(( $(stat -c %s $infile) - $(stat -c %s $outfile) ))
4

get10 test 8 <<<'Hello world'
rld!

printf %b ${test[@]/#/\\x}
Hello Wo⛶

get10 test 24 <<<'Hello World!'
printf %b ${test[@]/#/\\x}
Hello World!

(最后打印的字符是

\n
!;)

最终的二进制演示:

get10 test 256 < <(gzip <<<'Hello world!')

printf '%b' ${test[@]/#/\\x} | gunzip 
Hello world!

printf "  %s %s %s %s  %s %s %s %s    %s %s %s %s  %s %s %s %s\n" ${test[@]}
  1F 8B 08 00  00 00 00 00    00 03 F3 48  CD C9 C9 57
  28 CF 2F CA  49 51 E4 02    00 41 E4 A9  B2 0D 00 00
  00                    

注意!! 这工作正常并且非常快,而读取的字节数保持较低,甚至处理大文件也是如此。例如,这可以用于文件识别。但要将文件拆分为较大的部分,您必须使用

split
head
tail
和/或
dd

© www.soinside.com 2019 - 2024. All rights reserved.