12

If I have a string with nonprintable characters, new lines, or tabs, is there a way I can use echo to print this string and show codes for these characters (e.g., \n for new line, \b for backspace)?

  • 2
    you can use printf command, see man printf – PersianGulf Sep 9 '14 at 1:24
  • 12
    e- ohce *rimshot* – David Richerby Sep 9 '14 at 13:00
  • 2
    printf '%q' "$str" will print the string escaped. I'm not adding this as an answer since it's not the same form of escaping that echo -e uses -- rather, it's intended to be eval-safe -- but (1) it's good enough if your goal is human readability, (2) it's good enough if your goal is safe shell generation, and (3) echo -e is Evil And Wrong anyhow (if you read the POSIX spec, you'll see that printf is the preferred way to do string formatting, and echo -e is not guaranteed by baseline POSIX at all). – Charles Duffy Sep 9 '14 at 19:31
6

There are a lot of ways to do this. The most portable two that I know of are sed and od - they're both POSIX.

printf '\n\r\b\t\033[01;31m' | sed -n l

It does like... read style escapes - C-style.

OUTPUT

$
\r\b\t\033[01;31m$

od is a little more configurable...

printf '\n\r\b\t\033[01;31m' |
od -v -w12 -t c -t a -A n

 \n  \r  \b  \t 033   [   0   1   ;   3   1   m
 nl  cr  bs  ht esc   [   0   1   ;   3   1   m

If you wanna know what all of those options do you can look in man od, but I specify I want two types of escapes - the -t c backslash escapes and the -t a named characters. The -w option used above is not POSIX-specified.

And here's a little shell function that will portably print out the octal values of each byte in its arguments - which, of course, od might handle as well with -t o:

proctal() (LC_ALL=C
    for a do while [ -n "$a" ] 
    do printf %o\\n "'$a"
    a=${a#?}; done; done)  

That's a simple one. This is a little more complicated. It should be able to do what the shell-specific printf -q implementations can, though.

bsq() (set -f; export LC_ALL=C IFS=\'
    for a do q=${a##*\'}; printf \'
    [ -n "${a#"$q"}" ] &&
        printf "%s'\''" ${a%\'*}
    printf "%s'\n'''''\n" "$q"; done |
    sed -n "/'''''"'/!H;1h;//!d;$!n;x;l' |
    sed -e :n -e '/\\$/N;s/.\n//;tn
        s/\([^\\]\\\(\\\\\)*\)\([0-9]\)/\10\3/g
        s/\\\\'"''/\\\\''"'/g;s/$$//'
)

Using out example string from earlier with a little additional:

bsq "$(printf '\n\r\'\''b\t\033[01;31m')"

OUTPUT

'\n\r\\'\''b\t\0033[01;31m'

It's only slightly different. You might notice there's an extra 0 and an extra \backslash. This is to allow for easy translation to a read or a %b printf argument. For example:

i=0
until [ $((i=$i+1)) -gt 5 ]
do touch "\%$i$(printf 'hey\b \t;\n\033 ')"
done   #just for ugly's sake

bsq * | eval "
    printf '<%b>\n' $(tr \\n \ )
" | tee /dev/fd/2 |
sed -n l

OUTPUT

<\%1he  ;
>
<\%2he  ;
>
<\%3he  ;
>
<\%4he  ;
>
<\%5he  ;
>
<\\%1hey\b \t;$
\033 >$
<\\%2hey\b \t;$
\033 >$
<\\%3hey\b \t;$
\033 >$
<\\%4hey\b \t;$
\033 >$
<\\%5hey\b \t;$
\033 >$
  • @StéphaneChazelas It will still only do single-byte chars, but hopefully its less likely to screw up now. – mikeserv Sep 9 '14 at 12:06
  • 1
    Let me get you to the 10k mark :) – Ramesh Sep 9 '14 at 12:23
  • Woohoo!!!!!!!!! – slm Sep 9 '14 at 16:41
  • @slm - let me guess.. You really liked the proctal? – mikeserv Sep 9 '14 at 16:44
  • @StéphaneChazelas - do you have any thoughts on bsq() - is it missing anything? I ask because I'm not entirely confident about the backslashes. – mikeserv Sep 10 '14 at 6:10
12

When you remove the -e switch to echo, in bash, and provided the xpg_echo option has not been enabled, it should print the strings as nothing more than their original text. So \n and \t would show up as literally that.

Example

$ echo "hi\t\tbye\n\n"
hi\t\tbye\n\n

$ echo -e "hi\t\tbye\n\n"
hi      bye

You can also use the printf built-in command of ksh93, zsh or bash to finagle this as well.

$ printf "%q\n" "$(echo -e "hi\t\tbye\n\nbye")"
$'hi\t\tbye\n\nbye'

(bash output shown above. There are some variations depending on the shell).

excerpt from help printf in bash

%q quote the argument in a way that can be reused as shell input

  • 1
    It's not printf %q that's truncating the trailing \ns, it's the $() output capture that strips them out. – ecatmur Sep 9 '14 at 9:14
  • @ecatmur - thanks I wrote that late at night and hadn't tried to debug it further. You'd be correct, it's the subshell that's stripping them off. Here's a reason why: unix.stackexchange.com/questions/17747/… – slm Sep 9 '14 at 12:09
  • ...anyhow, running printf inside of a subshell to capture its output is silly, since printf -v varname will put the output directly into a variable, no subshell involved. – Charles Duffy Sep 9 '14 at 19:33
  • @CharlesDuffy - I'm not following you, I'm not running printf in a subshell, I was running echo in one, purely to demonstrate that you can take the special character output from echo and convert it back to the escaped notation. – slm Sep 9 '14 at 19:40
  • @slm, that was a response to the first comment (well, the code that the first comment responded to), not the code as I found it now. If I inferred something inaccurate about prior edit state, my apologies. – Charles Duffy Sep 9 '14 at 19:41
5

You could use something like,

$ cat filename
Ramesh is testing
New Line        is

Now, you could do something like,

$ cat -A filename

Output

Ramesh is testing$
New Line ^Iis $

Another general testing without any files is as,

$ echo Hello$'\t'world. | cat -A

The above command yields the output as,

Hello^Iworld.$

References

  • 4
    Or, in non-GNU implementations of cat, e.g. MacOS/X, take your cat to the vet ... cat -vet – tink Sep 9 '14 at 2:20
3

With zsh, there are the (q), (qq), (qqq), (qqqq) variable expansion flags that can quote the variables in various ways:

$ a=$'a b\nba\bc\u00e9\0'

$ printf '%s\n' $a
a b
bcé

$ printf %s $a | od -vtc
0000000   a       b  \n   b   a  \b   c 303 251  \0
0000013

$ printf '%s\n' ${(q)a}
a\ b$'\n'ba$'\b'cé$'\0'

$ printf '%s\n' ${(qq)a}
'a b
bcé'

$ printf '%s\n' ${(qqq)a}
"a b
bcé"

$ printf '%s\n' ${(qqqq)a}
$'a b\nba\bcé\0'

$ (){local LC_ALL=C; print -r -- ${(qqqq)a}}
$'a b\nba\bc\303\251\0'

In your case, you'd probably want one of the last two.

1

od -c will print normal characters normally, but special characters (tab, newline, etc.) and unprintable characters as their printf escape code.

So:

$ echo -e asdf\\nghjk\\003foo | od -c -An
   a   s   d   f  \n   g   h   j   k 003   f   o   o  \n

The -An tells od to not output the address.

-1

Use the printf command. For example:

printf "Hello\nthis is my line

will output

Hello
this is my line
  • 1
    This is the same as echo -e, I'm looking for the reverse. – drs Sep 9 '14 at 13:24
  • can you give me specific example? – Lazuardi N Putra Sep 9 '14 at 14:52

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.