Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot pass space separated argument from shell script to C++ executable

Tags:

c++

bash

shell

csh

I am invoking an executable from inside a shell script. The scenario which I am coding is different, but I have written a dummy executable and a script to show what I am facing

Script :

#!/bin/sh -h

MYARGS=""

for arg in "$@"; do
  shift
  case $arg in
    *)
    pattern=" "
      if [[ $arg =~ $pattern ]]; then
        MYARGS="$MYARGS \"$arg\""
      else
        MYARGS="$MYARGS $arg"
      fi
      ;;
  esac
done
echo $MYARGS
./a.out $MYARGS

Program:

#include "iostream"

    int main (int argc, char *argv[])
    {
        for (int i = 0; i < argc; i++)
        {
            std::cout << argv[i] << std::endl;
        }
    }

Now, I don't want to use $@ as I have to filter some arguments to be passed to the main program. But, when I try the above piece with space separated string as argument I get following output:

Command : ./a.out "a b"
Output : 
./a.out
a b

Command: a.sh "a b"
Output :
"a b"
./a.out
"a
b"

The problem is that in second case arguments are automatically split and 'a' and 'b' come as different arguments even though I am preserving the quotes ""

What can be the solution to this?

like image 868
Nishikant Avatar asked Mar 27 '26 07:03

Nishikant


2 Answers

Once a quote gets into a bash variable, it is just a character. Don't confuse what you type with what the variable contains. In this regard, bash is no different from C.

const char* v = "a b";
printf("%s\n", v);    // => a b
const char* w = "\"a b\"";
printf("%s\n", w);    // => "a b"

Bash is just the same:

$ v="a b"
$ echo "$v"
a b
$ w="\"a b\""
$ echo "$w"
"a b"

In the above, I put quotes around my variable expansions, which you should always do. (Well, almost always. But do it unless you have a really good reason, and can explain your reason.)

The reason I quote the expansion is to prevent the contents of the string from being word split after the expansion. Word-splitting splits the string into separate words at whitespace (unless $IFS has been set to something different). That's it. It doesn't look through the string for special characters, because there aren't any. Because once a quote is in a string, it is just another character. (And the same goes for all other special characters, like $ and \.

You can (and people may recommend to you) use complicated constructs using eval or eval-like operations (like bash -c). Those will almost always end in grief. The simplest, best, and recommended solution is to use an array:

myargs=()

for arg in "$@"; do
  shift
  case "$arg" in
    *) myargs+=("$arg") ;;
  esac
done
echo "${myargs[@]}"
./a.out "${myargs[@]}"

Note that every quote in the above snippet is essential (except for the ones in case "$arg") and they all have the same purpose: to avoid word-splitting when the variable (or the array elements) is expanded. (The exception for the case word is because the shell does not word-split expansions in that context. But you don't need to remember that; just use the quotes as shown.)

like image 153
rici Avatar answered Mar 29 '26 21:03

rici


Just use xargs

#!/bin/bash

MYARGS=""

for arg in "$@"; do
  shift
  case $arg in
    *)
    pattern=" "
      if [[ $arg =~ $pattern ]]; then
        MYARGS="$MYARGS \"$arg\""
      else
        MYARGS="$MYARGS $arg"
      fi
      ;;
  esac
done
echo $MYARGS | xargs ./a.out
like image 40
Joy Allen Avatar answered Mar 29 '26 21:03

Joy Allen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!