Skip to main content
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

I have a bash shell variable containing a string formed of multiple words delimited by whitespace. The string can contain escapes, such as escaped whitespace within a word. Words containing whitespace may alternatively be quoted.

A shell variable that is used unquoted ($FOO instead of "$FOO") becomes multiple words but quotes and escapes in the original string have no effect.

How can a string be split into words, giving consideration to quoted and escaped characters?

Background

A server offers restricted access over ssh using the ForceCommand option in the sshd_config file to force execution of a script regardless of the command-line given to the ssh client.

The script uses the variable SSH_ORIGINAL_COMMAND (which is a string, set by ssh, that contains the command-line provided to the ssh client) to set its argument list before proceeding. So, a user doing

$ ssh some_server foo 'bar car' baz

will see the script execute and it will have SSH_ORIGINAL_COMMAND set to foo bar car baz which would become four arguments when the script does

set -- ${SSH_ORIGINAL_COMMAND}

Not the desired result. So the user tries again:

$ ssh some_server foo bar\ car baz

Same result - the backslash in the second argument needs to be escaped for the client's shell so ssh sees it. What about these:

$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz

Both work, as would a printf "%q" quoting wrapperprintf "%q" quoting wrapper that can simplify the client-side quoting.

Client-side quoting allows ssh to send the correctly quoted string to the server so that it receives SSH_ORIGINAL_COMMAND with the backslash intact: foo bar\ car baz.

However there is still a problem because set does not consider the quoting or escaping. There is a solution:

eval set -- ${SSH_ORIGINAL_COMMAND}

but it is unacceptable. Consider

$ ssh some_server \; /bin/sh -i

Very undesirable: eval can't be used because the input can't be controlled.

What is required is the string expansion capability of eval without the execution part.

I have a bash shell variable containing a string formed of multiple words delimited by whitespace. The string can contain escapes, such as escaped whitespace within a word. Words containing whitespace may alternatively be quoted.

A shell variable that is used unquoted ($FOO instead of "$FOO") becomes multiple words but quotes and escapes in the original string have no effect.

How can a string be split into words, giving consideration to quoted and escaped characters?

Background

A server offers restricted access over ssh using the ForceCommand option in the sshd_config file to force execution of a script regardless of the command-line given to the ssh client.

The script uses the variable SSH_ORIGINAL_COMMAND (which is a string, set by ssh, that contains the command-line provided to the ssh client) to set its argument list before proceeding. So, a user doing

$ ssh some_server foo 'bar car' baz

will see the script execute and it will have SSH_ORIGINAL_COMMAND set to foo bar car baz which would become four arguments when the script does

set -- ${SSH_ORIGINAL_COMMAND}

Not the desired result. So the user tries again:

$ ssh some_server foo bar\ car baz

Same result - the backslash in the second argument needs to be escaped for the client's shell so ssh sees it. What about these:

$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz

Both work, as would a printf "%q" quoting wrapper that can simplify the client-side quoting.

Client-side quoting allows ssh to send the correctly quoted string to the server so that it receives SSH_ORIGINAL_COMMAND with the backslash intact: foo bar\ car baz.

However there is still a problem because set does not consider the quoting or escaping. There is a solution:

eval set -- ${SSH_ORIGINAL_COMMAND}

but it is unacceptable. Consider

$ ssh some_server \; /bin/sh -i

Very undesirable: eval can't be used because the input can't be controlled.

What is required is the string expansion capability of eval without the execution part.

I have a bash shell variable containing a string formed of multiple words delimited by whitespace. The string can contain escapes, such as escaped whitespace within a word. Words containing whitespace may alternatively be quoted.

A shell variable that is used unquoted ($FOO instead of "$FOO") becomes multiple words but quotes and escapes in the original string have no effect.

How can a string be split into words, giving consideration to quoted and escaped characters?

Background

A server offers restricted access over ssh using the ForceCommand option in the sshd_config file to force execution of a script regardless of the command-line given to the ssh client.

The script uses the variable SSH_ORIGINAL_COMMAND (which is a string, set by ssh, that contains the command-line provided to the ssh client) to set its argument list before proceeding. So, a user doing

$ ssh some_server foo 'bar car' baz

will see the script execute and it will have SSH_ORIGINAL_COMMAND set to foo bar car baz which would become four arguments when the script does

set -- ${SSH_ORIGINAL_COMMAND}

Not the desired result. So the user tries again:

$ ssh some_server foo bar\ car baz

Same result - the backslash in the second argument needs to be escaped for the client's shell so ssh sees it. What about these:

$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz

Both work, as would a printf "%q" quoting wrapper that can simplify the client-side quoting.

Client-side quoting allows ssh to send the correctly quoted string to the server so that it receives SSH_ORIGINAL_COMMAND with the backslash intact: foo bar\ car baz.

However there is still a problem because set does not consider the quoting or escaping. There is a solution:

eval set -- ${SSH_ORIGINAL_COMMAND}

but it is unacceptable. Consider

$ ssh some_server \; /bin/sh -i

Very undesirable: eval can't be used because the input can't be controlled.

What is required is the string expansion capability of eval without the execution part.

Source Link
starfry
  • 7.8k
  • 8
  • 55
  • 70

Can bash expand a quoted and/or escaped string variable into words?

I have a bash shell variable containing a string formed of multiple words delimited by whitespace. The string can contain escapes, such as escaped whitespace within a word. Words containing whitespace may alternatively be quoted.

A shell variable that is used unquoted ($FOO instead of "$FOO") becomes multiple words but quotes and escapes in the original string have no effect.

How can a string be split into words, giving consideration to quoted and escaped characters?

Background

A server offers restricted access over ssh using the ForceCommand option in the sshd_config file to force execution of a script regardless of the command-line given to the ssh client.

The script uses the variable SSH_ORIGINAL_COMMAND (which is a string, set by ssh, that contains the command-line provided to the ssh client) to set its argument list before proceeding. So, a user doing

$ ssh some_server foo 'bar car' baz

will see the script execute and it will have SSH_ORIGINAL_COMMAND set to foo bar car baz which would become four arguments when the script does

set -- ${SSH_ORIGINAL_COMMAND}

Not the desired result. So the user tries again:

$ ssh some_server foo bar\ car baz

Same result - the backslash in the second argument needs to be escaped for the client's shell so ssh sees it. What about these:

$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz

Both work, as would a printf "%q" quoting wrapper that can simplify the client-side quoting.

Client-side quoting allows ssh to send the correctly quoted string to the server so that it receives SSH_ORIGINAL_COMMAND with the backslash intact: foo bar\ car baz.

However there is still a problem because set does not consider the quoting or escaping. There is a solution:

eval set -- ${SSH_ORIGINAL_COMMAND}

but it is unacceptable. Consider

$ ssh some_server \; /bin/sh -i

Very undesirable: eval can't be used because the input can't be controlled.

What is required is the string expansion capability of eval without the execution part.