What I\'d like to do is take, as an input to a function, a line that may include quotes (single or double) and echo that line exactly as it was provided to the function. For
If one's shell does not support pattern substitution, i.e. ${param/pattern/string}
then the following sed
expression can be used to safely quote any string such that it will eval
into a single parameter again:
sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"
Combining this with printf
it is possible to write a little function that will take any list of strings produced by filename expansion or "$@"
and turn it into something that can be safely passed to eval
to expand it into arguments for another command while safely preserving parameter separation.
# Usage: quotedlist=$(shell_quote args...)
#
# e.g.: quotedlist=$(shell_quote *.pdf) # filenames with spaces
#
# or: quotedlist=$(shell_quote "$@")
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
#
# eval "set -- $quotedlist"
# for str in "$@"; do
# # fiddle "${str}"
# done
#
# or like this:
#
# eval "\$a_command $quotedlist \$another_parameter"
#
shell_quote()
{
local result=''
local arg
for arg in "$@" ; do
# Append a space to our result, if necessary
#
result=${result}${result:+ }
# Convert each embedded ' to \' , then insert ' at the
# beginning of the line, and append ' at the end of
# the line.
#
result=${result}$(printf "%s\n" "$arg" | \
sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")
done
# use printf(1) instead of echo to avoid weird "echo"
# implementations.
#
printf "%s\n" "$result"
}
It may be easier (and maybe safer, i.e. avoid eval
) in some situations to use an "impossible" character as the field separator and then use IFS
to control expansion of the value again.