linux - 在 Linux 和 Mac OS X 中使用 bash 读取/写入二进制文件中的字节?

标签 linux macos bash binary hex

<分区>

在 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
}

关于linux - 在 Linux 和 Mac OS X 中使用 bash 读取/写入二进制文件中的字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23710724/

相关文章:

bash - 从终端将目录中的文件列表打印到文本文件(但不是文本文件本身)

linux - 如何设置nginx最大打开文件数?

c - 通过串行端口发送文件

macos - 如何使目录可写?

python - 如何将CUPS打印机绑定(bind)到用户?

bash - 将元数据标签作为变量传递给 FFMPEG

mysql - 如何从 Linux 上的 asp.net 核心应用程序迁移我的表

linux - 如何在目录中找到最新的文件夹,然后相应地创建完整路径?

macos - GGTS 3.6.4(OSX 64位)错误-系统属性http.nonProxyHosts已设置为本地

macos - 从 OS X 上的文件中删除内联空字节