How to echo an escaped string
Solution 1
In the script:
echo "${1//$/\\$}"
This is a simple string replacement build into the shell. The syntax is ${[VARIABLE_NAME]//[PATTERN]/[REPLACEMENT]}
for a global literal string replacement.
Not sure why you would want to do this. The script is actually just reporting the string you sent to it; \$sad\$test
evaluates to exactly the same string as '$sad$test'
, except the latter one uses single quotes to escape the $
.
Solution 2
I assume you want the string to be shown “shell quoted”.
The printf built-in command in ksh, bash, and zsh all understand the %q
format specifier. The actual output is not standardized and can vary depending on the input string (see below). Because of the variation in the form of the output, you may not be able to use the output from one shell with another (especially if your target shell is a less feature-rich shell like ash, dash, etc.).
zsh also has support for generating quotes directly in parameter expansions:
echo "${(q)1}" # backslashes, with $'' for unprintable/invalid characters
echo "${(qq)1}" # single quotes
echo "${(qqq)1}" # double quotes
echo "${(qqqq)1}" # $'' quoting
If you want to “roll your own”, the easiest and most reliable thing to do is to thing generate your own single quoting. A simple algorithm follows:
- Change any single quotes to the sequence
'\''
. - Wrap the result in single quotes.
This works for Bourne-like shells because single quoting is completely literal1. To represent a single quote, you end the current single quoted span, supply a backslash-escaped single quote and resume single quoting the rest of the string. The above algorithm is inefficient when dealing with runs of consecutive single quotes and single quotes at the beginning and end of the string, but it is very reliable and quite simple to implement.
1 The single quote in other languages often is not completely literal (at the very least, they often allow some limited backslash escaping). This is why it is critically important that you know all the quoting rules for the form of quoting you will be generating (i.e. in what language/context the quoted text will be used).
Here are some examples of printf %q
in the shells I have installed locally:
for s in /bin/ksh {,/opt/local}/bin/bash {,/opt/local}/bin/zsh
do
"$s" --version | head -1
"$s" -c 'printf "%q\n" "$@"' - \$sad\$test \'mixed\ quotes\" back\`tick
echo
done
Output:
version sh (AT&T Research) 1993-12-28 s+
'$sad$test'
$'\'mixed quotes"'
'back`tick'
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
GNU bash, version 4.2.10(2)-release (i386-apple-darwin10.7.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
zsh 4.3.9 (i386-apple-darwin10.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
zsh 4.3.12 (i386-apple-darwin10.7.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
We see that ksh seems to “like” quoting with single quotes, and uses the $''
form in some cases. The other shells seem to mostly use backslash escapes (though they will also use $''
if some characters are present).
Solution 3
Put single quotes around \$sad\$test
:
$ echo $SHELL
/bin/bash
$ ./test.sh '\$sad\$test'
\$sad\$test
Solution 4
By doing test.sh \$sad\$test
, the test.sh
script receives $sad$test
as its parameter, not \$sad\$test
. The reason for that is the shell that replaces \$
by $
before callingtest.sh
. If you want your script to receive \$sad\$test
, you should either do test.sh '\$sad\$test'
or test.sh "\\\$sad\\\$test"
.
Try this to convince yourself:
function cat1stpar() {
cat << EOF
$1
EOF
}
cat1stpar \$sad\$test
cat1stpar '\$sad\$test'
cat1stpar "\\\$sad\\\$test"
Also, in the general case, using echo $1
is dangerous. If you do:
./test.sh "aaa bbb"
./test.sh "aaa bbb"
The output will be:
aaa bbb
aaa bbb
and not:
aaa bbb
aaa bbb
If you want the 2nd output, you shoud at least do echo "$1"
which will make sure echo
receives a single parameter, not as much as there is words in $1. For a complete word discussion, see the IFS variable in the shell man.
It is also said that doing printf "%s\n" "$1"
is safer than echo "$1"
because echo
changes some chars before printing, but I have not fully understand it yet.
Edit:
About echo
changing chars, I just understood it. Try:
echo "-n"
printf "%s\n" "-n"
./test.sh "-n"
cat1stpar "-n"
Related videos on Youtube
Admin
Updated on September 18, 2022Comments
-
Admin over 1 year
How can I echo an escaped string that contains $ in Bourne Shell?
user@server:~$ cat test.sh #!/bin/sh echo $1 user@server:~$ ./test.sh \$sad\$test $sad$test
I want it to return an escaped version like this:
user@server:~$ ./test.sh \$sad\$test \$sad\$test
I have tried doing tricks with sed and awk but no luck. Any guidance would be greatly appreciated.
-
Chris Johnsen over 12 yearsThat still only handles
$
. If you only use backslash escaping, many other characters also need escaping: backslash, single and double quotes, back quote, semicolon, ampersand, less- and greater-than, asterisk, question mark, etc. The list is pretty much unbounded since newer shells/features often give special meaning to (unquoted) characters that were not previously special. The only safe thing to do would be to escape every character (though you could probably get away with not escaping the alphanumerics). -
amphetamachine over 12 yearsNo. The safe thing to do would be to learn proper quoting techniques and string safety.
-
amphetamachine over 12 yearsChances are, if you're shell-escaping something you're about to do something extremely dangerous with it like pass it through
eval
. -
jfg956 over 12 yearsThis is not the solution. This only
echo
a value doing substitution. It does not change the fact that thetest.sh
script receives$sad$test
as the value of $1. -
dlamblin over 6 yearsThis is a fantastic thorough answer that deserves to be the correct answer.
-
mcandre over 6 yearsUnfortunately
%q
is missing from the printf in MINIX's default sh,sh
.