0

I have trouble using a command line executable. I need to pass a potentially empty string to a program from bash, but the string is stored in a bash variable.

The (minimal working example of the C++) program is:

// Compile with g++ main.cpp -o soexec

#include <iostream>

int main(int argc, char** argv) {
  // shows what the program arguments are
  for(size_t ind = 1 ; ind < argc; ++ind)
    std::cout << "  argv[" << ind << "]={" << argv[ind] << "}" << std::endl;

  // real application code should go here
  return EXIT_SUCCESS;
}

When I use the program without variables, I got the following, as expected:

$ ./soexec --paramName ""
    argv[1]={--paramName}
    argv[2]={} # empty string, as wanted
$ EMPTY_VARIABLE=
$ ./soexec --paramName "${EMPTY_VARIABLE}"
    argv[1]={--paramName}
    argv[2]={} # empty string, as wanted

However, if the command lines arguments are stored in a variable, I get an issue

$ VARIABLE_NAME=
$ ARGS="--paramName $VARIABLE_NAME"
$ ./soexec $ARGS
  argv[1]={--paramName}
  # no argv[2]!

I also tried

$ VARIABLE_NAME=\"\"
$ ARGS="--paramName $VARIABLE_NAME"
$ ./soexec $ARGS
  argv[1]={--paramName}
  argv[2]={""} # this time the string is no longer empty, it contains the quote...

How can in pass a potentially empty string in bash to a command-line program ?

Note: this question has been marked as a duplicate of When should I wrap quotes around a shell variable?. However, it not only about quote in shell variable, it is about variable stored in another variable.

PS: for more context, my initial (and now resolved) problem was: What's the best way to pass an empty string as argument to boost::program_options?

GabrielGodefroy
  • 198
  • 1
  • 8
  • Not quite: Since you are using bash, `./soexec $ARGS` would invoke word splitting on the passed argument, and your executable would see two parameters. Only if you run it as `./soexec "$ARGS"`, you would see a single argument. – user1934428 Sep 02 '22 at 08:27
  • 1
    Note also that `VARIABLE_NAME=\"\"` does not create an empty variable. It stores a string of length 2 into the variable. This is because the backslash tells bash to not consider the quote as a special character. – user1934428 Sep 02 '22 at 08:29

1 Answers1

1

The problem is not really storing the argument in a variable, but expanding it before its actual use by including it in the $ARGS string.

So my first suggestion would be to avoid that string and passing the arguments directly to the program, when possible:

./soexec --paramName "$VARIABLE_NAME"

However, when this is not possible (or for any reason you want to use the ARGS variable), then it's better to use a bash array (tutorial), instead of a string, so that each variable can be expanded individually. For this, you use "${your_array[@]}" ([@] to retrieve all elements and the quotes to keep the spaces in each, if present).

declare -a ARGS
ARGS[0]="--paramName"
ARGS[1]="$VARIABLE_NAME"
./soexec "${ARGS[@]}"

If you don't like using the indices, you can also just use the += operator to append values to the array.

declare -a ARGS
ARGS+=("--paramName")
ARGS+=("$VARIABLE_NAME")
# equivalent to
# ARGS+=("--paramName" "$VARIABLE_NAME")
./soexec "${ARGS[@]}"
Miguel
  • 2,130
  • 1
  • 11
  • 26
  • 1
    Using an array instead of a variable solves my problem, thanks. The program has a large number of arguments; that's why they are stored in a dedicated variable. Another use case would be if the program had to be called more than once in the same script. – GabrielGodefroy Sep 02 '22 at 07:40