What's the opposite of od(1)?

后端 未结 5 956
被撕碎了的回忆
被撕碎了的回忆 2020-12-10 15:41

Say I have 8b1f 0008 0231 49f6 0300 f1f3 75f4 0c72 f775 0850 7676 720c 560d 75f0 02e5 ce00 0861 1302 0000 0000, how can I easily get a binary file from that without copying+

5条回答
  •  被撕碎了的回忆
    2020-12-10 15:56

    All the present answers refer to the convenient xxd -r approach, but for situations where xxd is not available or convenient here is a more portable (and more flexible, but more verbose and less efficient) solution, using only POSIX shell syntax (it also compensates for odd-number of digits in input):

    un_od() {
        printf -- "$(
            tr -d '\t\r\n ' | sed -e 's/^(.(.{2})*)$/0\1/' -e 's/\(.\{2\}\)/\\x\1/g'
        )"
    }
    

    By the way: you don't specify whether your input is big-endian or little-endian, or whether you want big/little-endian output. Usually input such as in your question would be big-endian/network-order (e.g. as created by od -t x1 -An -v), and would be expected to transform to big-endian output. I presume xxd just assumes that default if not told otherwise, and this solution does that too. If byte-swapping is needed, how you do the byte-swapping also depends on the word-size of the system (e.g. 32bit, 64bit) and very rarely the byte-size (you can almost always assume 8bit bytes - octets - though).

    The below functions use a more complex version of the binary -> od -> binary trick to portably byteswap binary data, conditional on system endianness, and accounting for system word-size. The algorithm works for anything up to 72bit word-size (because seq -s '' 10 -> 12345678910 doesn't work):

    if { sed --version 2>/dev/null || :; } | head -n 1 | grep -q 'GNU sed'; then
        _sed() { sed -r "${@}"; }
    else
        _sed() { sed -E "${@}"; }
    fi
    
    sys_bigendian() {
        return $(
            printf 'I' | od -t o2 | head -n 1 | \
                _sed -e 's/^[^ \t]+[ \t]+([^ \t]+)[ \t]*$/\1/' | cut -c 6
        )
    }
    
    sys_word_size() { expr $(getconf LONG_BIT) / 8; }
    
    byte_swap() {
        _wordsize=$1
        od -An -v -t o1 | _sed -e 's/^[ \t]+//' | tr -s ' ' '\n' | \
            paste -d '\\' $(for _cnt in $(seq $_wordsize); do printf -- '- '; done) | \
            _sed -e 's/^/\\/' -e '$ s/\\+$//' | \
            while read -r _word; do            
                _thissize=$(expr $(printf '%s' "$_word" | wc -c) / 4)
                printf '%s' "$(seq -s '' $_thissize)" | tr -d '\n' | \
                    tr "$(seq -s '' $_thissize -1 1)" "$_word"
            done
        unset _wordsize _prefix _word _thissize
    }
    

    You can use the above to output file contents in bigendian format regardless of system endianness:

    if sys_bigendian; then
        cat /bin/sh
    else
        cat /bin/sh | byte_swap $(sys_word_size)
    fi
    

提交回复
热议问题