-1

My scheme is the following:

I have a shell script that executes a command which calls a C program :

name=$1
StringWithSpaces=$name
command="someprogram.out $StringWithSpaces $otherarguments"
$command

where name is a string with spaces s.a. "String With Spaces" passed to the shell from another python script.

My problem is that when I read that argument in C, it is passed as several arguments instead of just one. I have tried $@, $* and all that stuff. I have also tried to make a function in C that separate the several argv[i] within the StringWithSpaces one, but I am a bit stuck. I wish I could read the variable in C just as a single argument, to make the program as simple as I can.

This is the exact shell code (bash):

#!/bin/bash

#$1 Nombre de la base de datos
#$2 $3 gps coordinates
#$4 geoDistance (radio en km)
#$5 imDownload (a 0: solo se descargan GPS, a 1 también imágenes y tags)
#$Disabled keywords (opcional, lista de keywords separados por comas) 

#Generamos el base path
BASE_DIR=`pwd`
#BASE_DIR=${BASE_DIR%/*}
EXE_DIR=$BASE_DIR/FlickrAPI/bin
DB_PATH=$BASE_DIR/data

#Exportamos las librerías, necesario para que funcione el código
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${BASE_DIR}/FlickrAPI/lib

cont=1;

#General DB
name=$1
gps="$2 $3"
geoDistance=$4
numImagesDB=3751
ncores=400;
imDownload=$5

dbDir=$DB_PATH/$name
mkdir "$dbDir";

rm -rf "$dbDir"/imagesDB
rm -rf "$dbDir"/tagsDB
rm -rf "$dbDir"/gps.txt

mkdir "$dbDir"/imagesDB
mkdir "$dbDir"/tagsDB
#tidx=`seq 7 $#`;
#keywords="";
#for ((i=7;i<=$#;i++))
#do
#  keywords=$keywords" "${!i};
#done
keywords=$6;
export LD_LIBRARY_PATH=/home/user/anaconda2/lib/
command="$EXE_DIR/get_GPS_bimestral $dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords"
echo $command
$command
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
javier
  • 33
  • 3
  • 2
    Have you tryed to pass the variable with space inside quotes? For example: `someprogram.out "$StringWithSpaces" $otherarguments` – JuMoGar Jan 30 '19 at 09:36
  • @JuMoGar: Assigning to `command` and executing `$command` causes two expansions, defeating a single quoting. – Eric Postpischil Jan 30 '19 at 09:43
  • @EricPostpischil I do use bash. – javier Jan 30 '19 at 09:56
  • Possible duplicate of [How to execute a bash command stored as a string with quotes and asterisk](https://stackoverflow.com/questions/2005192/how-to-execute-a-bash-command-stored-as-a-string-with-quotes-and-asterisk) – Sander De Dycker Jan 30 '19 at 09:56
  • @javier just call the C program placing the args in a string as I do in my answer – bruno Jan 30 '19 at 09:59
  • `name = $1` is probably incorrect. You can't have spaces in the assignment. Also see [Assignment of variables with space after the (=) sign?](https://stackoverflow.com/q/26971987/608639) – jww Jan 30 '19 at 10:00
  • @EricPostpischil Just modified the question and added the exact actual code sorry. – javier Jan 30 '19 at 10:00

2 Answers2

1

Put the command in an array with:

Command=(someprogram.out "$StringWithSpaces" "$otherarguments")

When array is expanded in quotes using @ to request all its members, as in "${Command[@]}", then bash expands its array member to a single word. So the desired strings with spaces are kept as single strings.

Here is a sample script:

#!/bin/bash -e

function ShowArguments()
{
    for argument in "$@"; do
        echo "Argument:  $argument"
    done
}

Argument1="abc def"
Argument2="pdq xyz"

Command=(ShowArguments "$Argument1" "$Argument2")

echo "${Command[@]}"
"${Command[@]}"

The output from the above is:

ShowArguments abc def pdq xyz
Argument:  abc def
Argument:  pdq xyz

You may want "$otherarguments" to be $otherarguments. Or, if it contains a string with spaces that should be kept as a string, you should handle it the same way, as an array that is expanded with ${otherarguments[@]} in quotes. Here is an example with quoting of one of the variables used to hold arguments:

#!/bin/bash -e

function ShowArguments()
{
    for argument in "$@"; do
        echo "Argument:  $argument"
    done
}

Argument1="Single argument with multiple words"
Argument2=("Multiple argument" with various "numbers of words")

Command=(ShowArguments "$Argument1" "${Argument2[@]}")

echo "${Command[@]}"
"${Command[@]}"

It produces:

ShowArguments Single argument with multiple words Multiple argument with various numbers of words
Argument:  Single argument with multiple words
Argument:  Multiple argument
Argument:  with
Argument:  various
Argument:  numbers of words

After that, you might want to replace echo with a fancier command that quotes its arguments, to show the proper quoting to the user. But that is an aesthetic or user-interface choice; it will not affect the command executed.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

My problem is that when I read that argument in C, it is passed as several arguments instead of just one.

in your script replace

command="$EXE_DIR/get_GPS_bimestral $dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords"
echo $command
$command

by

command="$EXE_DIR/get_GPS_bimestral \"$dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords\""
echo $command
echo $command | bash

Explanations :

if the shell script b is :

./a.out "$* 3 4"

and c.c is

#include <stdio.h>

int main(int argc, char ** argv)
{
  printf("argc = %d, argv[1] = '%s'\n", argc, argv[1]);
}

then :

pi@raspberrypi:/tmp $ ./b 1 2
argc = 2, argv[1] = '1 2 3 4'

the C program receives only one arg, that is ok

but if the script is modified to be for instance :

command="./a.out \"$* 3 4\""
echo $command
$command

that doesn't work :

pi@raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3 4"
argc = 5, argv[1] = '"1'

so if you want to store in a var to echo then execute it you can do :

command="./a.out \"$* 3 4\""
echo $command
echo $command | bash

execution:

pi@raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3 4"
argc = 2, argv[1] = '1 2 3 4'

of course if you want for example 4 received as a separate argument just put it outside the string :

command="./a.out \"$* 3\" 4"
echo $command
echo $command | bash

execution :

pi@raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3" 4
argc = 3, argv[1] = '1 2 3'
bruno
  • 32,421
  • 7
  • 25
  • 37
  • This does not provide the means to construct a command with arguments expanded from variables, some of which contain strings containing spaces which should be kept as single arguments. – Eric Postpischil Jan 30 '19 at 10:39
  • @EricPostpischil what ? for instance $* is expanded and any other $aze used to make the command will be too. The OP want to get all in **one** argument – bruno Jan 30 '19 at 10:43
  • No, they want their “String with Spaces” to be passed as one argument, along with other arguments that are passed as multiple arguments. The goal is to keep an argument that contains internal spaces as one argument, but to pass multiple arguments. E.g., given `Path Containing Spaces`, `SwitchA`, and `SwitchB`, they want to call their program with `Path Containing Spaces` as one argument, `SwitchA` as another argument, and `SwitchB` as another argument. They do not want to pass `Path Containing Spaces SwitchA SwitchB` as one argument. – Eric Postpischil Jan 30 '19 at 10:50
  • @EricPostpischil so like the last example of my edited answer ? where is the problem ? Whatever, the OP has several solutions, this is the main – bruno Jan 30 '19 at 10:53
  • Your last example does not have a command in which any argument is expanded from a variable. It has a command provided as a quoted string. That is not what is desired. What is desired is that there are (at least) two variables: A and B. A is a string with spaces, and B contains other arguments. The goal is to set `command` so that `$command` results in executing the program with the contents of A passed as a single argument and the contents of B passed as additional arguments. – Eric Postpischil Jan 30 '19 at 10:56
  • Thus if A is “p q” and B is “r s”, the program should receive “p q” as one argument, “r” as another argument, and “s” as another argument. – Eric Postpischil Jan 30 '19 at 10:57
  • @EricPostpischil so `command = "exec \"$A\" $B"` then `echo $command | bash` and _exec_ will receive 3 args the first being "p q" the second "r" and the last "s" – bruno Jan 30 '19 at 11:00
  • That is not in the answer. Also, passing text expanded from user input to a shell script like that is a security problem; the user could have included malicious text crafted to cause the shell to execute other things. Additionally, the solution given in my answer preserves some other things that are problems for quoting, such as quotation marks inside strings. – Eric Postpischil Jan 30 '19 at 11:10