Cygwin下Bash写入32位整数到二进制文件出现8字节异常
我需要在二进制文件中写入头部信息,其他操作(如二进制合并)用批处理完成,但批处理无法直接写入二进制,只能写十六进制,所以改用Cygwin下的Bash脚本。尝试后发现,虽然能写入32位整数,但实际写入8字节而非预期的4字节,每个字节后都跟着0x0D。同时,写入的字节序也不符合预期,我需要在位置388处写入84de4e00(对应整数5168772),但当前结果不对。另外我的Windows10+Cygwin环境没有xxd,无法用相关方案。
我的Bash脚本:
HEXSTR=`printf "%08x" $1` BYTE1=`echo -n "$HEXSTR" | cut -c 1-2` BYTE2=`echo -n "$HEXSTR" | cut -c 3-4` BYTE3=`echo -n "$HEXSTR" | cut -c 5-6` BYTE4=`echo -n "$HEXSTR" | cut -c 7-8` echo $HEXSTR echo $BYTE1 echo $BYTE2 echo $BYTE3 echo $BYTE4 echo -ne "\x$BYTE4\x$BYTE3\x$BYTE2\x$BYTE1" | dd of="header.bin" bs=1 seek=388 conv=notrunc
测试参数为5168772时,终端输出:
C:\size.sh 5168772 004ede84 0 4e de 84 8+0 records in 8+0 records out 8 bytes (8 B) copied, 0.0002827 s, 28.3 kB/s
文件十六进制视图显示写入了00 4e de 84,但每个字节后都带0d。
错误原因与修复方法
1. 多余0x0D字节的问题
这是Cygwin默认的DOS/Windows换行转换机制导致的:当通过管道传递数据时,系统会自动将LF(0x0A)转换为CRLF(0x0D+0x0A),你的echo -ne输出的二进制数据被误识别为文本,触发了转换。另外echo本身可能在某些环境下会额外添加换行符,进一步加剧问题。
修复方案:用printf替代echo输出二进制数据,printf的转义更可靠,不会自动添加换行,也能避免转换问题。
2. 字节提取错误
你的脚本中用cut提取字节时出现了截断(从终端输出看BYTE1是 0而非预期的00),这是因为cut结合echo的方式容易受空格或环境变量的影响。改用Shell字符串直接截取更准确可靠。
3. 字节序验证
整数5168772的十六进制是0x004EDE84,小端序要求字节顺序为84 DE 4E 00,你的脚本里的\x$BYTE4\x$BYTE3\x$BYTE2\x$BYTE1逻辑是对的,但因为前面的字节提取错误导致结果不对。
修复后的脚本
#!/bin/bash # 确保参数是整数 if ! [[ "$1" =~ ^[0-9]+$ ]]; then echo "请输入整数参数" exit 1 fi # 生成8位十六进制字符串(小写) HEXSTR=$(printf "%08x" "$1") # 直接截取字符串,无需cut # HEXSTR格式为:字节1(高位) 字节2 字节3 字节4(低位) # 小端序需要反转:字节4 字节3 字节2 字节1 BYTE4=${HEXSTR:6:2} # 第7-8位字符 BYTE3=${HEXSTR:4:2} # 第5-6位字符 BYTE2=${HEXSTR:2:2} # 第3-4位字符 BYTE1=${HEXSTR:0:2} # 第1-2位字符 # 用printf直接输出二进制,避免echo的转换问题 printf "\x$BYTE4\x$BYTE3\x$BYTE2\x$BYTE1" | dd of="header.bin" bs=1 seek=388 conv=notrunc,nocreat
关键改进点
- 用
$(...)替代反引号`...`,这是更现代、更可靠的命令替换方式。 - 用Shell字符串截取
${HEXSTR:offset:length}替代cut,避免管道带来的错误。 - 用
printf输出二进制数据,彻底解决换行转换导致的0x0D问题。 - 添加了参数合法性检查,避免非整数输入。
dd添加nocreat参数,防止误创建不存在的文件(可选)。
内容的提问来源于stack exchange,提问作者user301880

