Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question
add comment

4 Answers

up vote 1 down vote accepted

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 $.

share|improve this answer
    
That 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). –  Chris Johnsen Aug 9 '11 at 5:48
    
No. The safe thing to do would be to learn proper quoting techniques and string safety. –  amphetamachine Aug 9 '11 at 7:35
2  
Chances are, if you're shell-escaping something you're about to do something extremely dangerous with it like pass it through eval. –  amphetamachine Aug 9 '11 at 7:42
    
This is not the solution. This only echo a value doing substitution. It does not change the fact that the test.sh script receives $sad$test as the value of $1. –  jfgagne Aug 9 '11 at 10:39
add comment

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:

  1. Change any single quotes to the sequence '\''.
  2. 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).

share|improve this answer
add comment

Put single quotes around \$sad\$test:

$ echo $SHELL
/bin/bash
$ ./test.sh '\$sad\$test'
\$sad\$test
share|improve this answer
add comment

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"
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.