> On 1/22/2012 7:15 PM, Ed Morton wrote:
>> On 1/22/2012 1:42 PM, Stephane Chazelas wrote:
>> <snip>
>>> main() {
>>> c=1
>>> passargs=
>>> for i do
>>> if ...; then
>>> passargs="$passargs \"\${$c}\""
>>
>> Why would the above not just be:
>>
>> passargs="$passargs \"$i\""
>>
>> $i is the current argument when the "if" condition is tested, $c is the argument
>> at the position determined by the number of times the "if" condition has succeeded.
>
> Ignore that last sentence, don't know what I was thinking. I still don't see why
> $i isn't enough though.
Because then you bring in escaping issues (which is the big reason to
avoid eval in the first place).
What if i contains stuff like this:
$TERM
a"
The stuff going into eval will look like this:
"$TERM" a" # unwanted expansion and syntax error!
Stephane did this the smart way: generating code which references
the positional parameters, rather than generating code in which
their text is already interpolated.
The \" \" quotes are there just to prevent word splitting.
The code going into eval looks like
"${1}" "${2}" "${3}" ... # refs to the parameters, not the text itself!
Of course, the parameters he doesn't want are omitted.
The curly braces are overkill, by the way. This should be enough,
but maybe it's less readable:
# generate "$1" "$2" "$3" ...
passargs="$passargs \"\$$c\""
This is like the difference between these:
# computed variable name
eval "$VAR=\$VAL" # evaled code: FOO=$VAL # good!
# versus
eval "$VAR=\"$VAL\"" # evaled code: FOO="<unsafe stuff>" # bad!
I don't think the trick applies here very easily because your output
parameter list does not necessarily consist of items which are parameters in
the original list. Two parameters -af 3 become three parameters -a -f 3.
You could solve this problem by generating variables:
gencounter=$((counter + 1))
genvar=GEN$gencounter
eval GEN$counter=\$value # $value holds datum we want.
passargs="$passargs \"\$GEN$gencounter\""
now the evaled code ends up being
"$GEN0" "$GEN1" "$GEN2" ...
The only problem with this is pollution.
The working code is not any simpler in the end.
The first cut doesn't quite work:
function main {
printf 'main %s\n' "$@"
passargs=
gc=0
while getopts "a:f:bx" opt
do
printf '\t opt="%s", OPTARG="%s"\n' "$opt" "$OPTARG"
case $opt in
[af] ) eval "GEN$gc=-$opt; GEN$((gc + 1))=\$OPTARG"
passargs="$passargs \"\$GEN$gc\" \"\$GEN$((gc + 1))\""
gc=$(( gc + 2 ))
esac
done
shift $(( OPTIND - 1 ))
eval sub "$passargs" "$@"
}
The problem is with the "$@" in the eval. Watch what happens with $TERM:
$ ./main.sh -f d -a d\" '$TERM' asdf
main -f
main d
main -a
main d"
main $TERM
main asdf
opt="f", OPTARG="d"
opt="a", OPTARG="d""
sub -f
sub d
sub -a
sub d"
sub xterm
sub asdf
subarg="-f"
subarg="d"
subarg="-a"
subarg="d""
subarg="xterm"
subarg="asdf"
Oops! To fix that we need a second loop where we add more stuff to passargs:
function main {
printf 'main %s\n' "$@"
passargs=
gc=0
while getopts "a:f:bx" opt
do
printf '\t opt="%s", OPTARG="%s"\n' "$opt" "$OPTARG"
case $opt in
[af] ) eval "GEN$gc=-$opt; GEN$((gc + 1))=\$OPTARG"
passargs="$passargs \"\$GEN$gc\" \"\$GEN$((gc + 1))\""
gc=$(( gc + 2 ))
esac
done
shift $(( OPTIND - 1 ))
i=1
for remaining ; do
passargs="$passargs \"\$$i\""
i=$((i + 1))
done
printf 'what is being evaled: %s\n' "$passargs"
eval sub "$passargs"
}
$ ./main.sh -f d -a d\" '$TERM' asdf
main -f
main d
main -a
main d"
main $TERM
main asdf
opt="f", OPTARG="d"
opt="a", OPTARG="d""
what is being evaled: "$GEN0" "$GEN1" "$GEN2" "$GEN3" "$1" "$2"
sub -f
sub d
sub -a
sub d"
sub $TERM
sub asdf
subarg="-f"
subarg="d"
subarg="-a"
subarg="d""
subarg="$TERM"
subarg="asdf"
Oh man, gensyms in shell code, *slap forehead*.
Make my latte de-eval-inated, please ...