2

How do I unescape strings outputted by strace? I see some advices to use printf '%b' for that, but that doesn't work correctly for me in some cases. Consider echo -ne '\037\061':

$ strace -e write echo -ne '\037\061'
write(1, "\0371", 21)                    = 2
+++ exited with 0 +++

Now, if I feed this into printf:

$ printf "%b" "\0371" | xxd -p
f9

While in reality it was:

$ echo -ne '\037\061' | xxd -p
1f31

Obviously, printf %b interprets \0371 as \371 (as it should, according to the man). This means printf is not really suitable for decoding strace output. What should I use instead?

1 Answer 1

2

tl;dr – see "conclusion" at the very end.


What happens here is quite interesting.

First of all echo -e acts like this (from man 1 echo):

\0NNN
byte with octal value NNN (1 to 3 digits)

This means 0 in \037 is not an octal digit, it's just a part of \0 prefix that indicates consecutive octal input. Your \037 may be \0037 and now only the second 0 is an octal digit.

Still within echo -e \061 is equivalent to \0061. This is equivalent to 1, a literal character meaning "one".

Now it seems that the part of strace output you want to decode should be decoded this way:

write(1, "\0371", 21)
          ^     prefix that indicates consecutive octal output
           ^^^  three digits of octal output
              ^ literal character meaning "one"

So the prefix here is not \0 but \.

You use printf "%b" "\0371". The general form is printf FORMAT [ARGUMENT]… and this is what man 1 printf says about FORMAT being %b:

%b
ARGUMENT as a string with \ escapes interpreted, except that octal escapes are of the form \0 or \0NNN

As you can see the prefix is \0, like in echo -e. The interpretation now goes like this:

printf "%b" "\0371"
             ^^    prefix that indicates consecutive octal input
               ^^^ three octal digits

This explains your wrong result. However the same manual states that inside FORMAT octal digits are interpreted in a slightly different way:

\NNN
byte with octal value NNN (1 to 3 digits)

The prefix is \ exactly as in the output of strace. Additionally strace seems to take care whenever the next character can be interpreted as an octal digit. Compare:

$ strace -e write echo -ne '\037'
write(1, "\37", 1)                      = 1
+++ exited with 0 +++

to

$ strace -e write echo -ne '\037\101'
write(1, "\37A", 21)                    = 2
+++ exited with 0 +++

to

$ strace -e write echo -ne '\037\061'
write(1, "\0371", 21)                    = 2
+++ exited with 0 +++

Note the leading 0 in the last case. It's there to avoid \371 which would be interpreted as a single byte.

It looks like you could use printf to decode strace output, but you should pass it as FORMAT, not ARGUMENT:

$ printf "\0371" | xxd -p
1f31

But then other sequences interpreted inside FORMAT (e.g. %b) would get you in trouble, so it's better to stick to ARGUMENT. I played with sed to turn \ into \0 in proper cases, it got cumbersome fast; then I realized %b as FORMAT will also interpret other sequences in ARGUMENT! In my opinion it's a dead end.


Conclusion: it's way better to change the behavior of strace. Try hex output:

$ strace -xx -e write echo -ne '\037\061'
write(1, "\x1f\x31", 21)                    = 2
+++ exited with 0 +++

Then

$ printf "%b" "\x1f\x31" | xxd -p
1f31
1
  • Thanks for the detailed explanation. I don't like the idea of using -x though: it makes the output a lot harder to read (now I need to decode everything!). strace -xe write echo -ne '\037z' results in "\x1f\x7a" instead of much more useful "\x1fz", which makes it a lot harder to look for the syscal I'm after in the strace output
    – torvin
    May 27, 2018 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.