3

i want to turn a string like

AaAa

into

a string like this

%<41>%<61>%<41>%<61>

Simple enough with the programming languages i am familar with, but with bash i can't get get the piping right to do what i am trying to do:

  • split string into char array
  • turn each char into hex
  • wrap each hex value into %<FF>
  • concat string

this is my current way which gets me half way there:

echo -n "AaAa" | od -A n -t x1
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Dbl
  • 5,634
  • 3
  • 41
  • 66

5 Answers5

3

If you are already using od,

printf "%%<%s>" $(od -A n -t x1<<<"AaAa")

For an all-bash without od,

while read -r -N 1 c; do printf "%%<%02X>" "$( printf "%d" \'$c )"; done <<< AaAa

The downside of this approach is that it spawns a subshell for every character, and assumes ASCII/UTF8.


edit

@Shawn pointed out that you don't need the subshell -

while read -r -N 1 c; do printf "%%<%02X>" \'$c; done <<< AaAa

I noticed that these are leaving the string terminator in your output, though, and realized I could eliminate that and the read by assigning the data to a variable and using the built-in parsing tools.

$: x=AaAa && for((i=0;i<${#x};i++)); do printf "%%<%02X>" \'${x:i:1}; done; echo
%<41>%<61>%<41>%<61>
Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
  • As written, this won't run on the POSIX shell (`/bin/sh`). It also has an upper bound on the size of the input. These may or may not be issues. – ikegami Jul 23 '21 at 20:44
  • If you want *just* bash, c.f. https://stackoverflow.com/questions/67870452/take-first-16-character-and-covert-it-into-hex-string/67874337#67874337 – Paul Hodges Jul 23 '21 at 20:44
  • 2
    `bash` was explicitly mentioned. – Paul Hodges Jul 23 '21 at 20:44
  • 3
    You don't need the second `printf` in the pure bash version... `printf "%%<%02X>" "'$c"` works. – Shawn Jul 23 '21 at 21:01
  • This variant of the first should work for larger inputs: `od -A n -t x1 <<<"AaAa" | xargs printf "%%<%s>"` – Gordon Davisson Jul 24 '21 at 02:19
  • The ARG_MAX limit only applies to external programs, but printf is a bash built-in, so the size of the hexdump shouldn't be a problem (unless it so big that it does not fit into RAM). – Socowi Jul 24 '21 at 13:56
  • I was also getting the string terminator. Edited. – Paul Hodges Jul 26 '21 at 13:53
2

A simple Perl substitution would do the trick:

echo -n AaAa | perl -pe's/(.)/ sprintf "%%<%02X>", ord($1) /seg'

Shorter:

echo -n AaAa | perl -ne'printf "%%<%02X>", $_ for unpack "C*"'

In both cases, the output is the expected

%<41>%<61>%<41>%<61>

(No trailing line feed added. If you want one, append ; END { print "\n" }.)

ikegami
  • 367,544
  • 15
  • 269
  • 518
1

You could use :

echo -n AaAa | perl -ne 'for $c (split//) { printf("%%<%02X>", ord($c)); }'

Output

%<41>%<61>%<41>%<61>
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • is the result of this processable through pipes as well? since you replied first i would flag yours as answer then, but right now i am getting odd behavior in WSL – Dbl Jul 23 '21 at 20:21
  • @Dbl Yeah, it should work fine through pipes. I actually used WSL when writing the oneliner. What's odd about the result? – Ted Lyngmo Jul 23 '21 at 20:27
  • the formatting is off, but maybe thats a wsl problem – Dbl Jul 23 '21 at 20:42
  • @Dbl Hmm, it looks as expected for me. How is the formatting off? – Ted Lyngmo Jul 23 '21 at 20:45
  • 1
    WSL is not an OS. I use Ubuntu (over WSL), and it works fine. There's nothing OS-specific about that command (though you'd have to adjust quoting for the Windows `cmd` shell). – ikegami Jul 23 '21 at 20:46
1

You can pipe to sed to wrap each byte in %<> and then remove the whitespace.

echo -n "AaAa" | od -A n -t x1 | sed -E -e 's/[a-z0-9]+/%<&>/g' -e 's/ //g'
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

Maybe awk

echo -n "AaAa" |
od -A n -t x1 |
awk 'BEGIN { ORS = "" } { for (i = 1; i <= NF; i+=1) print "%<"$i">"}'
Jetchisel
  • 7,493
  • 2
  • 19
  • 18