如何使用Bash编写二进制文件?

27

我的问题是我需要创建一个包含这些精确字节的文件: 48, 00, 49, 00.

我无法使用C、perl或其他脚本语言(目标设备是嵌入式设备)。我尝试使用awk,在桌面上确实可以工作:

# awk  'BEGIN{ printf "%c%c%c%c", 48, 00, 49, 00 }' | hexdump
0000000 0030 0031                              
0000004

然而目标平台运行的是busybox v1.13.2,这段代码在那里不能正常工作。那里的awk版本不会输出ascii "0"(所有其他值都可以)。

您有什么建议?

6个回答

43

你可以使用以下命令:

echo -n -e \\x48\\x00\\x49\\x00 > myfile

谢谢。同样的问题只发生在路由器内部可以访问bash。Perl和C是梦想。我使用该设备与其他从设备进行RS485通信,交换完美无缺! - Orlov Const
谢谢。美丽的事物,永远是一种喜悦。 - daparic
为避免错误,请注意echo -n -e \\x48\\x00\\x49\\x00 这些是十六进制值。因此,如果您想添加LF(ASCii十进制10),则应发送\x0a。 - Carles Mateo

6

你可以尝试使用echo命令,它也允许任意的ASCII字符(这些数字是八进制数)。

echo -n -e \\0060\\0000\\0061\\0000  | hexdump

...并且以十六进制表示,这是可能的吗? - elcuco
不,echo只接受八进制数。但是你要求一个特定的十六进制序列(我甚至已经为你将它们转换成了八进制)。如果你不确定如何转换,请在ascii的man页面中查找不同的十六进制序列。它会给你一个包含十进制、十六进制和八进制数字的表格;-)。 - flolo
2
仅供参考,它可以接受十六进制。只需使用\xNN,其中N为十六进制数字。 - Velocibadgery

6

根据POSIX AWK标准规定,使用%c格式向AWK的printf传递0可能导致未指定的行为。然而... POSIX echo也非常有限,尽管八进制和十六进制说明符(以及-n)可以在GNU echo和BASH内置中使用...但它们在其他地方可能无法正常工作。为了最大化在所有POSIX系统上获得一致的行为,最好使用shell命令行的printf,而不是这两者之一。

$ printf '\060\000\061\000' | od -An -tx1
 30 00 31 00

这对我来说看起来很奇怪...也许你想输出0x48、0x00、0x49、0x00——这在八进制中看起来像一个相当好的飞行员数字:

$ printf '\110\000\111\000' | od -An -tx1
 48 00 49 00

4

我需要在旧版Android的shell中使用busybox从十六进制编写二进制文件。在我的使用情况下,这个带有重定向的printf命令起了作用。

将二进制数据以十六进制形式写入:

# busybox printf '\x74\x65\x73\x74' > /sdcard/test.txt

以十六进制形式展示结果:

# busybox hexdump -C /sdcard/test.txt
00000000  74 65 73 74                                       |test|
00000004

以ASCII码方式显示结果:

# cat /sdcard/test.txt
test

为什么需要使用 dd 命令?为什么不能使用 busybox printf '\x74\x65\x73\x74' > /sdcard/test.txt 呢? - Keith Thompson
哇,你说得对!我确信我试过了。我尝试了所有这些答案和其他十几个答案,但都失败了。原来如此简单。可能当时有其他变量干扰了。我会修改我的答案。谢谢! - BuvinJ

1

输出整数的一些通用函数:

le16 () { # little endian 16 bit binary output 1st param: integer to 2nd param: file 
  v=`awk -v n=$1 'BEGIN{printf "%04X", n;}'`
  echo -n -e "\\x${v:2:2}\\x${v:0:2}" >> $2
}

le32 () { # 32 bit version
  v=`awk -v n=$1 'BEGIN{printf "%08X", n;}'`
  echo -n -e "\\x${v:6:2}\\x${v:4:2}\\x${v:2:2}\\x${v:0:2}" >> $2
}

使用此命令为iio数据流生成音频WAV文件头:

channels=2
bits_per_sample=16
let "block_align = channels * bits_per_sample / 8"    

wave_header () { # pass file name and data size as parameters; rest are constants set elsewhere
  data_size=$2
  let "RIFFsize = data_size + 44 - 8"
  let "bytes_per_sec = sampleHz * block_align"

  echo -e -n "RIFF" > $1
    le32 $RIFFsize $1
  echo -e -n "WAVEfmt " >> $1
    le32 16 $1  # format size
    le16 1 $1   #format tag: 1 = PCM
    le16 $channels $1
    le32 $sampleHz $1
    le32 $bytes_per_sec $1
    le16 $block_align $1
    le16 $bits_per_sample $1   # bits per sample
  echo -e -n "data" >> $1
    le32 $data_size $1
}

将Linux iio ADC数据捕获到WAV文件中:

sampleHz=8000
milliseconds=15  # capture length 

cd /sys/bus/iio/devices/iio:device0

cat /sys/bus/iio/devices/trigger0/name > trigger/current_trigger

echo 0 > buffer/enable

echo 0 > scan_elements/in_voltage0_en  #
echo 1 > scan_elements/in_voltage1_en  #  
echo 1 > scan_elements/in_voltage2_en  # 
echo 0 > scan_elements/in_voltage3_en  #

echo $sampleHz > sampling_frequency
sampleHz=`cat sampling_frequency`  # read back actual sample rate

let "buffer_length = block_align * sampleHz * milliseconds / 1000"
echo $buffer_length > buffer/length

cd $HOME

echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable
wave_header data.wav $buffer_length
head -c $buffer_length /dev/iio:device0 >> data.wav  # LE16 data
echo 0 > /sys/bus/iio/devices/iio:device0/buffer/enable

0

我不知道busybox里面有什么,但这个可能有效,因为printf比awk更小。

$ printf "%c%c%c%c" 48 0 49 0 | hexdump

这是输出结果:

$ printf "%c" 1 | hexdump
0000000 0031                    

1
我根据我看到的编辑了你的答案,但这不是我要求的。这会打印出0x31,这是字符“1”的ASCII值。 - elcuco
如果您将1替换为H和I,则可以获得您想要的值0x48,0x49 - 而不是使用0和1的十进制值48/49。问题在于如何使零字节不终止输出。上述内容可转换为printf "%c%c%c%c" 72 0 73 0 | hexdump,用于输出0x48 0x00 0x49 0x00。 - starturtle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接