<分区>
在 Bash 中,您无法轻松地操作二进制文件中的数据,因为您不能将 ASCII 字符 0 存储在 Bash 变量中。这使得很难向文件写入或从文件读取任意字节。
此外,如果能够在 Linux 或 OS X/BSD 上进行字符、十进制、十六进制和二进制之间的转换,而不需要格式化和解析像 hexdump 这样的程序的输出,这可能是不一致的或甚至可以跨平台使用。
是否存在用于 Bash 的 Linux 和 OS X 兼容例程库,以便更轻松地处理二进制数据和在基数之间进行转换?
(注意:这是一个 self 回答的问题,但无论如何请提供比我想出的更好的答案,如果你有的话。)
有,因为我写了一个。这个 Bash 函数库简化了文件中的直接字节操作,而不是必须以另一种格式(如十六进制)表示整个文件才能使用它。
以下任何或所有函数都可以放在任何 Bash 脚本的顶部,或通过 Bash 脚本中的“源”加载。它们已经在 Debian Linux 和 OS X 上进行了测试。请注意,如果您想免除参数验证,它们可以大大精简。
希望这些例程对您有所帮助;虽然它们可能无法完全满足您在脚本中的需求,但它们有望成为您所需要的起点或构建 block 。 (我也认识到像 Python 或 Perl 这样的语言可能会使类似的任务变得更容易,但这个解决方案坚持使用 Bash 和外部命令。)
decToHex:将单字节十进制值转换为等效的十六进制值
hexToDec:将单字节十六进制值转换为等效的十进制值
hexToBin:将单字节十六进制值转换为二进制字符串
binToDec:将单字节二进制字符串值转换为十进制
binToHex:将单字节二进制字符串值转换为十六进制
charToDec:将单个字符转换为相应的十进制值
charToHex:将单个字符转换为相应的十六进制值
decToChar:将单字节十进制值转换为对应的字符
hexToChar:将单字节十六进制值转换为对应的字符
readchars:从文件中读取一个或多个字符(字节)
readcharDec:从文件中读取一个字符并转换为对应的十进制值
readcharHex:从文件中读取一个字符并转换为对应的十六进制值
writechars:将一个或多个字符(字节)写入文件
writecharDec:将单字节十进制值对应的字符写入文件
writecharHex:将单字节十六进制值对应的字符写入文件
writecharsHex:将十六进制字符串对应的字符写入文件
# BashByter functions for working with 8-bit data in Bash
decToHex () {
# converts single-byte decimal value to hexadecimal equivalent
# arg: decimal value from 0-255
# out: two-digit hex value from 00-FF
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ( $(printf %d "$1" 2> /dev/null) == $1 ) \
&& ( $1 -ge 0 ) && ( $1 -le 255 ) ]] || return 21
# args are valid
printf %02X "$1"
}
hexToDec () {
# converts single-byte hexadecimal value to decimal equivalent
# arg: two-digit hex value from 00-FF
# out: decimal value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
printf %d "0x$1"
}
hexToBin () {
# converts single-byte hexadecimal value to binary string
# arg: two-digit hex value from 00-FF
# out: binary string value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
for n in 0 1; do
if [[ ${1:n:1} == "0" ]]; then b="0000"
elif [[ ${1:n:1} == "1" ]]; then b="0001"
elif [[ ${1:n:1} == "2" ]]; then b="0010"
elif [[ ${1:n:1} == "3" ]]; then b="0011"
elif [[ ${1:n:1} == "4" ]]; then b="0100"
elif [[ ${1:n:1} == "5" ]]; then b="0101"
elif [[ ${1:n:1} == "6" ]]; then b="0110"
elif [[ ${1:n:1} == "7" ]]; then b="0111"
elif [[ ${1:n:1} == "8" ]]; then b="1000"
elif [[ ${1:n:1} == "9" ]]; then b="1001"
elif [[ ${1:n:1} == "A" ]]; then b="1010"
elif [[ ${1:n:1} == "B" ]]; then b="1011"
elif [[ ${1:n:1} == "C" ]]; then b="1100"
elif [[ ${1:n:1} == "D" ]]; then b="1101"
elif [[ ${1:n:1} == "E" ]]; then b="1110"
elif [[ ${1:n:1} == "F" ]]; then b="1111"
fi
echo -n $b
done
}
binToDec () {
# converts single-byte binary string (8 bits) value to decimal
# arg: binary string (each char is 0 or 1) up to 8 bits
# out: decimal value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
dec=0
bits=$1
while (( ${#bits} < 8 )); do
bits="0$bits"
done
for n in {0..7}; do
[[ ${bits:$n:1} == "0" || ${bits:$n:1} == "1" ]] || return 21
(( dec+=( ${bits:$n:1} * ( 2**(7-$n) ) ) ))
done
echo -n $dec
}
binToHex () {
# converts single-byte binary string (8 bits) value to hex
# arg: binary string (each char is 0 or 1) up to 8 bits
# out: hex value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
dec=0
bits=$1
while (( ${#bits} < 8 )); do
bits="0$bits"
done
for n in {0..7}; do
[[ ${bits:$n:1} == "0" || ${bits:$n:1} == "1" ]] || return 21
(( dec+=( ${bits:$n:1} * ( 2**(7-$n) ) ) ))
done
printf %02X $dec
}
charToDec () {
# converts single character to corresponding decimal value
# stdin OR arg: one character
# [arg overrides stdin; stdin is required for NUL (0) or LF (0x0A)]
# out: decimal value from 0-255
#exit: 8=extraneous arg, 9=invalid stdin,
# 11=missing stdin/arg, 21=invalid arg
[[ ( ! -t 0 ) && $1 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $2 ]] && return 8; [[ $1 ]] || return 11; }
# arg/stdin is potentially valid (additional check below)
charX="$1X"; [[ $1 ]] || charX="$(cat; echo -n 'X';)"
[[ ${#charX} -le 2 ]] || return $(( $([[ $1 ]]; echo $?) ? 9 : 21 ))
# above line verifies that arg/stdin is valid
[[ ${#charX} -ne 2 ]] && { echo -n 0; return 0; }
echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n'
}
charToHex () {
# converts single character to corresponding hexadecimal value
# stdin OR arg: one character
# [arg overrides stdin; stdin is required for NUL (0) or LF (0x0A)]
# out: decimal value from 0-255
#exit: 8=extraneous arg, 9=invalid stdin,
# 11=missing stdin/arg, 21=invalid arg
[[ ( ! -t 0 ) && $1 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $2 ]] && return 8; [[ $1 ]] || return 11; }
# arg/stdin is potentially valid (additional check below)
charX="$1X"; [[ $1 ]] || charX="$(cat; echo -n 'X';)"
[[ ${#charX} -le 2 ]] || return $(( $([[ $1 ]]; echo $?) ? 9 : 21 ))
# above line verifies that stdin/arg is valid
[[ ${#charX} -ne 2 ]] && { echo -n "00"; return 0; }
printf %02X $(echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n')
}
decToChar () {
# converts single-byte decimal value to equivalent character
# arg: decimal number from 0-255
# out: one character
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ( $(printf %d "$1" 2> /dev/null ) == $1 ) \
&& ( $1 -ge 0 ) && ( $1 -le 255 ) ]] || return 21
# args are valid
echo -n -e "\x$(printf %02X "$1")"
}
hexToChar () {
# converts single-byte hexadecimal value to corresponding character
# arg: two-digit hexadecimal number from 00-FF
# out: one character
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
echo -n -e "\x$1"
}
readchars () {
# read one or more characters from a file
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# arg3: (optional) # of chars to read (default is until end of file)
# out: sequence of characters
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11
[[ $4 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
[[ $3 ]] && { [[ ( $(printf %d "$3" 2> /dev/null) == $3 ) \
&& ( $3 -ge 0 ) ]] || return 23; }
# args are valid
dd if="$1" bs=1 skip=$(($2)) $([[ $3 ]] && echo -n "count=$3") \
2> /dev/null
}
readcharDec () {
# read one character from file & convert to equivalent decimal value
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# out: decimal value from 0-255
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2
[[ $1 ]] || return 11
[[ $3 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
# args are valid
charX="$(dd if="$1" bs=1 skip=$(($2)) \
count=1 2> /dev/null; echo -n X)"
[[ ${#charX} -gt 1 ]] || { echo -n 0; return 0; }
echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n'
}
readcharHex () {
# read one character from file & convert to corresponding hex value
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# out: two-digit hex value from 00-FF
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2
[[ $1 ]] || return 11
[[ $3 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
# args are valid
charX="$(dd if="$1" bs=1 skip=$(($2)) \
count=1 2> /dev/null; echo -n X)"
[[ ${#charX} -gt 1 ]] || { echo -n "00"; return 0; }
printf %02X $(echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n')
}
writechars () {
# write one or more characters (bytes) to file
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before writing)
# arg3 OR stdin: sequence of characters
# [stdin required if writing NUL (0) or trailing LF (0x0A) chars]
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1,
# 13=missing stdin/arg3, 22=invalid arg2
[[ $1 ]] || { [[ -t 0 ]] || cat > /dev/null; return 11; }
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) && \
( $2 -ge 0 ) ]] || { [[ -t 0 ]] || cat > /dev/null; return 22; } }
[[ ( ! -t 0 ) && $3 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $4 ]] && return 8; [[ $3 ]] || return 13; }
# args are valid
if [[ -t 0 ]]; then
echo -n "$3" | \
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
else
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
fi
}
writecharDec () {
# write corresponding character of single-byte decimal value into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: decimal number from 0-255
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
# out: nothing
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
[[ ( $(printf %d "$3" 2> /dev/null) == $3 ) \
&& ( $3 -ge 0 ) && ( $3 -lt 255 ) ]] || return 23
# args are valid
echo -n -e "\x$(printf %02X "$3")" | \
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
}
writecharHex () {
# write corresponding character of single-byte hex value into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: two-digit hexadecimal number from 00-FF
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
[[ $(printf %02X "0x$3" 2> /dev/null) == \
$(echo -n "$3" | tr [a-z] [A-Z]) ]] || return 23
# args are valid
echo -n -e "\x$3" | \
dd of="$1" bs=1 seek=$2 conv=notrunc 2> /dev/null
}
writecharsHex () {
# write corresponding characters of hex values into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: string of two-digit hexadecimal numbers from 00-FF
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
p=0
offset=$2
len=${#3}
while (( p < len )); do
outByte=${3:$p:2}
[[ $(printf %02X "0x$outByte" 2> /dev/null) == \
$(echo -n "$outByte" | tr [a-z] [A-Z]) ]] || return 23
# args are valid
echo -n -e "\x$outByte" | \
dd of="$1" bs=1 seek=$offset conv=notrunc 2> /dev/null
(( p += 2 ))
(( offset++ ))
done
}