简体   繁体   中英

Bash: Passing parameters to a C program via shell variable. How to deal with spaces and quoting characters?

My Bash script sends a set of (multiple) parameters to a C program.

This is my C program :

$ cat main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char **argv )
{

    printf("Parameter 1 is: %s \n", argv[1]);
    printf("Parameter 2 is: %s \n", argv[2]);
    return 0;
}

Once compiled (as MyCProgram ), it behaves OK:

MyCProgram -f "/path/to/file/with spaces"
Parameter 1 is: -f
Parameter 2 is: /path/to/file/with spaces

But, when trying to send to it parameters via shell variable :

$ var='-f "/path/to/file/with spaces" '
$ MyCProgram $var
Parameter 1 is: -f
Parameter 2 is: "/path/to/file/with
$ MyCProgram "$var"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram '$var'
Parameter 1 is: $var
Parameter 2 is: (null)
$ MyCProgram "$(echo $var)"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram "$(echo "$var")"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram "$(echo '$var')"
Parameter 1 is: $var
Parameter 2 is: (null)
$ MyCProgram '$(echo "$var")'
Parameter 1 is: $(echo "$var")
Parameter 2 is: (null)
$ var="-f '/path/to/file/with spaces' "
$ MyCProgram $var
Parameter 1 is: -f
Parameter 2 is: '/path/to/file/with

How can I obtain the proper behavior , this is, same as when run without shell variables?

Notes :

Upon Craig Estey answer , this works fine:

$ var=(-f "/file with spaces")
$ MyCProgram "${var[@]}"

This method works, but only if I manually (explicitly) assign the value to var . Assuming var is already assigned (ie: via input read or file read), I would say the problem here is transferring it to a bash array variable. So:

$ var='-f "/file with spaces"'
$ var2=( $var )
$ MyCProgram "${var2[@]}"
Parameter 1 is: -f
Parameter 2 is: "/file

The problem is still there.

You probably want to use a bash array variable

Do the following to set the variable:

var=(-f "/file with spaces")

Then, invoke your program:

MyCProgram "${var[@]}"

var='-f "/path/to/file/with spaces" '

In this situation, the contents of var are shell syntax.

To process shell syntax, we can therefore use the eval function:

eval "MyProgram $var"

This will construct the argument

MyProgram -f "/path/to/file/with spaces" 

and then evaluate it as a little shell script.

Needless to say, there are security implications. We are trusting the inputs that went into the construction of script contained in var . For that reason, eval should be shunned as much as possible.

When we introduce eval , we have to think about: who controls the inputs that go into the string being passed to eval ? Could someone malicious sneak in something to get eval to run arbitrary code?

If the construction of var is under your control, and either has no untrusted inputs, or those inputs are dealt with properly, then this may be an appropriate solution.

Without specifying delimiter in xargs , it parses " and ' quotes and backslashes, so assuming var content is sane, you could do:

<<<"$var" xargs MyProgram

You could write your own tokenizer for var content - ex. split it on the first space and then remove the " quotes.

# split var on space
IFS=' ' read -r option file <<<"$var"
# remove quotes from file - ie. remove last and first character
file=${file%\"}
file=${file#\"}
# run your program
MyProgram "$option" "$file"

Would you please try the following:

var='-f "/path/to/file/with spaces" '
declare -a var2="( $var )"
./MyCProgram "${var2[@]}"

Output:

Parameter 1 is: -f 
Parameter 2 is: /path/to/file/with spaces 

Note that declare may have a potential risk as eval and you will need to take a control so that an arbitrary string is not fed to declare .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM