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)?
- 5,363
- 9
- 40
- 69
-
2you can use printf command, see man printf – PersianGulf Sep 09 '14 at 01:24
-
13`e- ohce` \*rimshot\* – David Richerby Sep 09 '14 at 13:00
-
3`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 09 '14 at 19:31
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
%qquote the argument in a way that can be reused as shell input
- 522,931
- 91
- 1,010
- 1,501
- 363,520
- 117
- 767
- 871
-
1It's not `printf %q` that's truncating the trailing `\n`s, it's the `$()` output capture that strips them out. – ecatmur Sep 09 '14 at 09: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: http://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char – slm Sep 09 '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 09 '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 09 '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 09 '14 at 19:41
-
@CharlesDuffy - not an issue, was just trying to understand what you were pointing out 8-) – slm Sep 09 '14 at 19:55
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 >$
- 57,448
- 9
- 113
- 229
-
@StéphaneChazelas It will *still* only do single-byte chars, but hopefully its less likely to screw up now. – mikeserv Sep 09 '14 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. – mikeserv Sep 10 '14 at 06:10
-
-
@BinaryZebra - i dont care either way, and was merely curious if it was useful. seems to be the same - the syntax coloring here is crazy. for handling code interpreted in quotes *(like `eval "command $(##...some long arg parser function)"`* i often wind up doing stuff like `"command $(#fu"!ing colors)"` – mikeserv Nov 16 '15 at 05:19
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** – tink Sep 09 '14 at 02: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.
- 522,931
- 91
- 1,010
- 1,501
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.
- 367
- 2
- 15
Use the printf command. For example:
printf "Hello\nthis is my line
will output
Hello
this is my line
- 133
- 9
- 213
- 1
- 6