How can I check the verbatim characters of a bash command string?
You could use cat
with the -A
option: from the manual:
-A, --show-all
equivalent to -vET
-E, --show-ends
display $ at end of each line
-T, --show-tabs
display TAB characters as ^I
-v, --show-nonprinting
use ^ and M- notation, except for LFD and TAB
So cat -A yourscrip.sh
will show you invisible and strange characters.
One option is to look at the characters you're trying to use with a hex viewer or editor. hexdump
is a good option if you are limited to the terminal.
$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000 5b 20 2d 66 20 2f 65 74 63 2f 6f 70 65 6e 76 70 |[ -f /etc/openvp|
00000010 6e 2f 63 6c 69 65 6e 74 2e 63 6f 6e 66 20 5d 20 |n/client.conf ] |
00000020 26 26 20 65 63 68 6f 20 74 72 75 65 0a |&& echo true.|
0000002d
You can see here that the space
, close-square-brace
, space
are correct - 0x20
, 0x5D
, 0x20
.
These values are ASCII codes, displayed in hexadecimal. Any value outside the range 0x20
- 0x7E
is not a "printable character" as far as ASCII is concerned, and most likely won't play well with command line interfaces.
Note: I copied your first "broken" line for use in the hexdump
example above, so something has replaced the not-an-ASCII-space with an ASCII space between your original source and your rendered question.
To repeat this, take the following steps:
- Type
hexdump -Cv <<"EOF"
and press Enter - Paste the text you would like to use
- Type
EOF
on a line of its own, and press Enter
Terminals and Command Line Interfaces don't handle special characters well - as you have discovered. If you aren't very careful with formatting documents, you will also have problems with Microsoft Word (and others) using "smart quotes", em-dashes, the list goes on...
Spot the difference: (the top is "smart quotes", the bottom is "straight quotes")
$ hexdump -Cv <<"EOF"
> “quoted string”
> EOF
00000000 e2 80 9c 71 75 6f 74 65 64 20 73 74 72 69 6e 67 |...quoted string|
00000010 e2 80 9d 0a |....|
00000014
Here, the open quotes are not a simple ASCII quote ("
), but are a Unicode / UTF-8 series - 0xE2
, 0x80
, 0x9C
, or U+201C
- which the terminal will not handle as you might expect.
Kiwy's suggestion of cat -A
also does the job:
$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$
Note: when using echo "..." | hd
, you stand a chance that bash will substitute parts of the string you are trying to inspect. This is particularly of concern when trying to inspect components of a script.
For example try:
$ echo "${USER}"
attie
$ echo "`whoami`"
attie
$ echo "$(whoami)"
attie
$ cat <<EOF
> ${USER}
> EOF
attie
These methods are replacing components with the relevant text. To avoid this, use one of the following approaches. Note the use of single quotes ('
), and a "quoted heredoc" ("EOF"
).
$ echo '${USER}'
${USER}
$ echo '`whoami`'
`whoami`
$ echo '$(whoami)'
$(whoami)
$ cat <<"EOF"
> ${USER}
> EOF
${USER}
echo "<your command>" | hd
should work. Look for backspace (0x08) or characters with codes >=80. echo "<your command>" | wc -b
and checking that the count matches what you see is also a good idea.
Copying stuff from files produced by anything with "Office" in its name is dangerous, because such software often takes the liberty to replace characters: in French, look out for double quotes replaced by "guillemets", in English for plain quotes replaced by their open/close equivalents. The hardest one I ever found was a 0-width non-breaking space in the middle of a file name (3 days of server downtime...).