3

I am trying to automate the swapping of characters 1 and 2, 3 and 4, etc, in a an arbitrary-length string of hex characters.

Example:

A627E39B

becomes:

6A723EB9

It seems like this should be possible using sed or one of the other *nix text utilities, but so far I have not been able to find any examples.

cuonglm
  • 153,898
Eet
  • 31

3 Answers3

8

You can use back-references:

echo A627E39B | sed 's/\(.\)\(.\)/\2\1/g'

This finds all occurrences of two characters and swaps them.

As glenn jackman pointed out, enabling extended regular expressions (-E w/ GNU, BSD, or toybox seds) avoids having to escape the parentheses:

echo A627E39B | sed -E 's/(.)(.)/\2\1/g'
Stephen Kitt
  • 434,908
4

There’s a tool that specifically, natively supports that function: dd with the swab (swap bytes) conversion:

% echo ABCD | dd conv=swab
BADC
0+1 records in
0+1 records out
5 bytes (5 B) copied, 0.000109277 s, 45.8 kB/s

You’ll want to add 2> /dev/null to discard the log output:

% echo ABCD | dd conv=swab 2> /dev/null
BADC

Warning: This will do pretty much what you want if you send it an even number of (printable) characters.  But, if you give it an odd number of printable characters — let’s say 2n+1 — then dd will take the newline at the end as the (2n+2)th character, and swap it with the (2n+1)th.  So,

% echo ABCDE | dd conv=swab 2> /dev/null

sends

                A B C D E \n

to dd, which gives you

                B A D C \n E

back; this will look like

BADC
E

You can work around this by specifying the -n option to echo to suppress the newline at the end (or just use printf without adding a newline).  If you’re displaying the output to the screen, then, for completeness, you should add a newline at the very end:

% echo -n ABCDE | dd conv=swab 2> /dev/null; echo
BADCE

% printf "%s" ABCDE | dd conv=swab 2> /dev/null; printf "\n"
BADCE
0

You can also use perl:

$ echo A627E39B | perl -CLS -nle 'print map {~~reverse} unpack "(a2)*"'
6A723EB9
cuonglm
  • 153,898