12

Is there a simple command to reverse an hexadecimal number?

For example, given the hexadecimal number:

030201

The output should be:

010203

Using the rev command, I get the following:

102030

Update

$ bash --version | head -n1
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
$ xxd -version
xxd V1.10 27oct98 by Juergen Weigert
$ rev --version
rev from util-linux 2.20.1
5
  • 2
    Please don't add the answer to your question.
    – cat
    Commented Nov 8, 2016 at 15:44
  • @cat I added the answer because the one that worked for me, is at the comments of the selected answer. But I'm ok removing it. Commented Nov 8, 2016 at 15:47
  • 2
    You could add it as an answer by clicking the "Answer this question" button below the answer box (self-answers are encouraged), and you should, just don't put it in the question.
    – cat
    Commented Nov 8, 2016 at 15:53
  • 2
    The C programmer in me wants to say "010203" is an octal number, not a hex number (0x10203)
    – infixed
    Commented Nov 8, 2016 at 16:18
  • @infixed You are not wrong, but I wanted an answer that treats 010203 as an hexadecimal, even though I do not use 0x Commented Nov 8, 2016 at 16:22

6 Answers 6

12

With fold + tac + tr :

$ echo 030201|fold -w2|tac|tr -d "\n"
010203
  • fold - split every 2 byte
  • tac - reverse cat
  • tr - remove newlines
11

If your system has a rev command.

hex=030201
new_hex=$(printf %s "$hex" | dd conv=swab 2> /dev/null | rev)

If it has a tac or tail -r command:

new_hex=$(echo "$hex" | fold -w 2 | tac | paste -sd '\0' -)

With zsh:

setopt extendedglob
new_hex=${(j[])${(s[]Oa)${hex//(#b)(?)(?)/$match[2]$match[1]}}}

(like in the dd approach: swap pairs of characters, split into list of individual characters (s[]), reverse the order (Oa) and join (j[])).

Or:

printf -v new_hex '%2$s%1$s' ${(s[]Oa)hex}

POSIXly:

new_hex=$(
  awk '
    BEGIN {
      hex = ARGV[1]; l = length(hex)
      for (i = 1; i < l; i += 2)
        new_hex = substr(hex, i, 2) new_hex
      print new_hex
    }' "$hex"
)

Or

new_hex=$(echo "$hex" |
  sed -e 'G;:1' -e 's/\(..\)\(.*\n\)/\2\1/;t1' -e 's/.//')

With perl:

new_hex=$(perl -le 'print reverse(shift =~ /../g)' -- "$hex")
4
  • 3
    I was going to suggest perl -F'(..)' -lane 'print reverse(@F)' :)
    – Sundeep
    Commented Nov 8, 2016 at 12:59
  • 1
    @Sundeep, nice. I didn't know one could use -F like that. (I can see it described in the split() manual now). Commented Nov 8, 2016 at 13:22
  • 1
    as far as I know, -F is basically splitting on $_.. apart from using regex like -F'/"\K\|(?=")/' one can specify number of splits as well... like -F'/:/,$_,2' ... use () if separator has to be captured as well
    – Sundeep
    Commented Nov 8, 2016 at 13:27
  • Instead of using paste you could use tr -d '\n'
    – AKHolland
    Commented Nov 10, 2016 at 21:35
8

You can convert it to binary, reverse the bytes, optionally remove trailing newlines rev <2.24, and convert it back:

$ xxd -revert -plain <<< 030201 | LC_ALL=C rev | tr -d '\n' | xxd -plain
010203

Using

$ bash --version | head -n1
GNU bash, version 4.3.42(1)-release (x86_64-redhat-linux-gnu)
$ xxd -version
xxd V1.10 27oct98 by Juergen Weigert
$ rev --version
rev from util-linux 2.28.2

This does not work if the string contains 00 (the NUL byte), because rev will truncate the output at that point, or 0a (newline), because rev reverses each line rather than the entire output.

8
  • 2
    I get 0102030a instead of 010203 Commented Nov 8, 2016 at 12:48
  • @l0b0 and too me,get 0102030a
    – Baba
    Commented Nov 8, 2016 at 12:49
  • 2
    @IñakiMurillo in your rev version 2.20.1; use this formate xxd -revert -plain <<< '030201' | LC_ALL=C rev | tr -d '\n'| xxd -plain
    – Baba
    Commented Nov 8, 2016 at 12:59
  • 2
    rev before version 2.24 have a new line bug . more info github.com/karelzak/util-linux/commit/…
    – Baba
    Commented Nov 8, 2016 at 13:33
  • 2
    It does not work if the hex string contains the '00' NUL byte. Commented Nov 9, 2016 at 8:43
6
perl -nE 'say reverse /(..)/g'

This reverts each hexadecimal line:

  • /(..)/g buils a list with the captured matches
4

(for the sake of completeness)

$ echo 030201 | grep -o .. | tac | paste -sd '' -
010203
1
  • 1
    This works if the '00' NUL byte is in the input. Commented Nov 9, 2016 at 8:54
1

Based on Ipor Sircer's answer https://unix.stackexchange.com/a/321867/337458 I would recommend this in your ~/.bashrc to have a nice command that you simply can call:

function hex_inverse() {
    echo ${1} | fold -w2 | tac | tr -d "\n" ; echo "" 
}

$> hex_inverse 030201

010203

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .