bash getopts broke my string

I was informed that a very very very simple web front end to a CLI I had written was not working. Ok, it wasn't shocking that it wasn't working, but the problem was beyond icky to fix. In the end it comes down to bash getopts dealing with a string differently, based on something. I'm not sure what.

Here is what happened. The php front I put together is simple. Very simple. You pull it up and it lists a set of servers. For each one you can click on a 'Start' link which then allows you to enter some command line arguments into a text box and click submit. The arguments are then passed to the command line tool, which does as it's told.

Not much happens to the arguments.

Once they arrive on the node where the script is to run I decode it the urlencoded version:

php -r "echo urldecode(\"${ARGUMENTS}\");"

The I attempt call the script after logging the the command as it will be executed, something like this:


/blah/blah/script ${ARGUMENTS} &> /tmp/$$.log &

This worked mostly but if the command lines included strings with white space it failed even with proper quoting! For example

-l -f 'string with "white space" inside' -k -y
would fail to run, because the bash builtin getopts would only send 'string to the script as the argument to -f. In other words it split along whitespace regardless of quotes.

I tried a bunch of stuff, like quoting, double quoting, quoting escapes, splitting the arguments into a bash array and then using ${A[@]}, changing IFS, but none of this helped.

In the end I just did the following:

echo "/blah/blah/script ${ARGUMENTS} &> /tmp/$$.log &" > /tmp/$$.sh
chmod u+x /tmp/$$.sh
/tmp/$$.sh

That worked.

It feels a bit kludgey, but seems to do the trick.

I'm still wondering why this happened, perhaps if it wasn't just one string of arguments it would be better. I also don't know why the same command runs when I cut and paste it directly from the log. There is likely some difference in the interactive bash environment.

\@matthias

Comments

my theory

I suspect that when the interpreter expands ${ARGUMENTS} it doesn't do a second pass to isolate the quoted strings as discrete arguments.

So if ARGUMENTS="foo 'bar baz' blah" the result is four tokens

"foo", "'bar", "baz'", "blah"

instead of the desired three

"foo", "bar baz", "blah"

By writing to a file and reinterpreting it, you're effectively doing that second pass.