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)?
6 Answers
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).
help printf
in bash
%q
quote the argument in a way that can be reused as shell input
-
1It's not
printf %q
that's truncating the trailing\n
s, it's the$()
output capture that strips them out.– ecatmurCommented Sep 9, 2014 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 ♦Commented Sep 9, 2014 at 12:09
-
...anyhow, running
printf
inside of a subshell to capture its output is silly, sinceprintf -v varname
will put the output directly into a variable, no subshell involved. Commented Sep 9, 2014 at 19:33 -
@CharlesDuffy - I'm not following you, I'm not running
printf
in a subshell, I was runningecho
in one, purely to demonstrate that you can take the special character output fromecho
and convert it back to the escaped notation.– slm ♦Commented Sep 9, 2014 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. Commented Sep 9, 2014 at 19:41
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 \b
ackslash. 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.– mikeservCommented Sep 9, 2014 at 12:06
-
1
-
-
-
@StéphaneChazelas - do you have any thoughts on
bsq()
- is it missing anything? I ask because I'm not entirely confident about the backslashes.– mikeservCommented Sep 10, 2014 at 6:10
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
-
4Or, in non-GNU implementations of cat, e.g. MacOS/X, take your cat to the vet ... cat -vet– tinkCommented Sep 9, 2014 at 2:20
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.
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.
Use the printf
command. For example:
printf "Hello\nthis is my line
will output
Hello
this is my line
-
1
-
e- ohce
*rimshot*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 thatecho -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, andecho -e
is not guaranteed by baseline POSIX at all).