What's the opposite of od(1)?
Asked Answered
S

5

11

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+pasting into a hex editor?

Somehow answered 27/4, 2009 at 19:17 Comment(2)
Write a simple script or program and execute it. 5-10 lines of C, or any other language. Done deal.Rotten
Here's the code golf question to this problem with lot's of solutions: #795527Portage
M
17

Use:

% xxd -r -p in.txt out.bin
Morphophoneme answered 27/4, 2009 at 19:27 Comment(0)
M
4

See xxd.

Mediant answered 27/4, 2009 at 19:20 Comment(0)
S
3

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., 32 bit, 64 bit) and very rarely the byte-size (you can almost always assume 8-bit 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 72-bit 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 big-endian format regardless of system endianness:

if sys_bigendian; then
    cat /bin/sh
else
    cat /bin/sh | byte_swap $(sys_word_size)
fi
Speaker answered 22/9, 2016 at 19:25 Comment(1)
@michael I now noticed your suggested edit which was already rejected by other reviewers. I think you maybe added the text part to fulfil the "edit must be more than X characters" limitation & the reviewers maybe only noticed that part (& suggested it wasn't needed). Your suggestion about extra backslashes in the code is correct though (and in fact there were 6 extra needed, not just 2) so I added them now. Just commenting to say thanks for catching that. I guess I code-tested with sed -E and then rewrote to avoid -E for portability, and missed escaping the first of the two -e args.Speaker
I
1

This version will work with binary format too:

cat /bin/sh \
| od -A n -v -t x1 \
| tr -d '\r' \
| xxd -r -g 1 -p1 \
| md5sum && md5sum /bin/sh

The extra '\r' is just if you're dealing with DOS text files... And process byte by byte to prevent endianness difference if running parts of a pipe on different systems.

Inebriant answered 16/3, 2016 at 20:5 Comment(0)
I
0

Here is the way to reverse "od" output:

echo "test" | od -A x -t x1 | sed -e 's|^[0-f]* ?||g' | xxd -r
test
Inebriant answered 2/8, 2014 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.