Ben Bacarisse <
ben.u...@bsb.me.uk>:
> Mike Sanders <mi...@porkchop.bsd> writes:
> <snip>
>> BACKUP_ARGS=$@
>
> The trouble with that is that you can't reliably recover the originals
> later. It works in many cases but it's fragile.
With POSIX compliant shells, you can use the function
"my_quote_words_for_shells" (as defined below) to store the
positional parameters and restore them later using "eval", "set"
and "shift":
For example,
set '' 'These are some funny positional parameters:' \
'backslashes (\),' 'dollar signs ($),' \
'single ('\'') and double (") quotes, backticks (`)' \
'braces ({, }}, brackets ([, ]), and parantheses,' \
'even semicolons (;), ampersands (&), and' \
'parameters looking like they were commands, like' \
'date; pwd; wc </etc/passwd', \
'parameters looking like command substitution:' \
'`date; pwd`, even trailing newlines are preserved:
' && shift
printf '%s\n' "$@"
BACKUP_ARGS="$(my_quote_words_for_shells "$@")"
# Reset the positional parameters:
set some positional parameters
printf '%s\n' "$@"
# Restore the original ones later using
# "eval", "set" and "shift":
eval "set '' $BACKUP_ARGS" && shift
printf '%s\n' "$@"
my_quote_words_for_shells()
(
# Outputs a (part of) a command line resembling a 'simple command'
# to be used by a POSIX-compliant shell.
#
# Usage:
#
# my_quote_words_for_shells words to be quoted...
#
# exit code: 0, if the command line has been successfully constructed,
# !=0, if the function failed to construct the command line.
#
# Examples:
#
# Put a word list resembling a command invocation into a command line:
#
# if command_line="$(my_quote_words_for_shells \
# program with parameters)"
# then
# # "eval" it:
# eval "$command_line"
# # or invoke a new shell:
# sh -c "$command_line" sh
# # or use it remotely:
# ssh us...@remote.host.example "$command_line"
# else
# # failed to compute the command line.
# fi
#
#
# Store the shell positional parameters in a variable and restore
# them later:
#
# if args="$(my_quote_words_for_shells "$@")"
# then
# # the positional parameters may be changed for any purpose
# # ...
#
# # restore the positional parameters:
#
# eval "set '' $args" && shift
# else
# # failed to save the positional parameters.
# fi
# "wordsep" contains a separator used for the list of the quoted
# words. The first need not to be preceded by a separater:
wordsep=
for word
do
# "$wordsep" separates each word (except the first one)
# from its precedessor:
printf '%s' "$wordsep"
if test -z "$word"
then
# The word is empty. Then the result is "''":
printf '%s' "''"
else
# The word is not empty.
while
{
prefix="${word%%\'*}"
word="${word#"$prefix"}"
# "$prefix" is the longest beginning part
# of "$word", that does not contain any
# apostrophes; it is removed from "$word".
# Conclusion: "$word" is either empty or
# begins with an apostrophe.
if test -n "$prefix"
then
# "$prefix" consists of one or more
# characters. Put them in between
# of two apostrophes:
printf \''%s'\' "${prefix}"
fi
test -n "$word" &&
{
# "$word" is not empty.
# Conclusion: "$word" begins with
# one or more apostrophes.
apostr="${word%%[!\']*}"
# "$apostr" ist the longest
# beginning part of "$word", that
# contains apostrophes only.
# Put it in between of two double
# quotes:
printf '"%s"' "${apostr}"
# Remove the beginning apostrophes
# from "$word":
word="${word#"$apostr"}"
# If nothing is left, we are done:
${word:+:} false
}
}
do
:
done
fi
# All following words (except the first one) have to be
# separated from their precedessor with a blank:
wordsep=' '
done
printf '\n'
)