1

I'm going nuts trying to figure out how to correctly pass arguments from a shell script to python when backticks are involved.

This is my ./foo.sh

#!/bin/bash
EXEC_SCRIPT="./foo.py -c  $1"
result=`${EXEC_SCRIPT}`
echo ${result}

This is my ./foo.py

#!/usr/bin/python
import argparse

ap = argparse.ArgumentParser()
ap.add_argument('-c',  required=True)

args,u = ap.parse_known_args()
args = vars(args)

print ("I GOT {}".format(args['c']))

I do:

./foo.sh ABC

and I get :

I GOT ABC

Perfect.

But then I do:

./foo.sh "Hello World"

And I get:

I GOT Hello

Trying to change the bash script to:

EXEC_SCRIPT="./foo.py -c  \"$1\""

Produces:

I GOT "Hello

None of this is an issue if I don't use backticks. Escaped quotes work great.

What am I missing?

What I really want is the python script should get my argument, whether its 1 word or 2 without quotes.

Further clarification: Thanks to Gordon and AK47 for making the same suggestion. It looks like the root cause is I am stuffing the command in a variable called EXEC_SCRIPT. Invoking the command directly inside the backticks works. My real script is more complex and EXEC_SCRIPT points to different values due to different conditions. What's a good way to achieve clean code which lets me figure out the right command and then invoke it at the end? Using a variable is logical, as I did, but it apparently doesn't work

user1361529
  • 2,667
  • 29
  • 61
  • 2
    You should use `$( )` instead of backticks, but that's not the actual problem here. The problem is you're storing the command string in a variable (`EXEC_SCRIPT`) before executing it, and there are all sorts of ways that causes problem. See [BashFAQ #50: "I'm trying to put a command in a variable, but the complex cases always fail!"](http://mywiki.wooledge.org/BashFAQ/050) and ["Why does shell ignore quotes in arguments passed to it through variables?"](https://stackoverflow.com/questions/12136948/why-does-shell-ignore-quotes-in-arguments-passed-to-it-through-variables). – Gordon Davisson Feb 10 '19 at 00:54
  • 1
    Solution: don't put the command in a variable, just execute it directly. – Gordon Davisson Feb 10 '19 at 00:54
  • because of the way my larger script runs, the variable can have different values and to keep code clean, I execute it at the end, whatever the value. – user1361529 Feb 10 '19 at 00:55
  • 3
    Then put the arguments in an array instead of in a string – that other guy Feb 10 '19 at 00:59
  • @thatotherguy can you expand? I did `EXEC_ARRAY[0]="./foo.py -c $1"` and `result=\`${EXEC_ARRAY[0]}\`` but it produced the same result – user1361529 Feb 10 '19 at 01:10
  • Okay, I really should read @GordonDavisson's FAQ link. It seems to say there is no good way to do variable stuffing. – user1361529 Feb 10 '19 at 01:11
  • 1
    @user1361529 Depending on exactly what you're doing, an array *might* work. But you need to store each argument (and the command itself) as separate elements of the array, rather than trying to store the command string into a single array element. Something like `EXEC_ARRAY=(./foo.py -c "$1")` and `result=$("${EXEC_ARRAY[@]}")` – Gordon Davisson Feb 10 '19 at 01:50
  • Great Krypton! The array approach worked! – user1361529 Feb 10 '19 at 02:00

2 Answers2

1

I have @that other guy to thank and @Gordon Davisson for the comment clarification to the suggestion.

Modifying foo.sh to execute an array instead of a string like so:

#!/bin/bash
EXEC_SCRIPT=(./foo.py -c "$1")
result=$("${EXEC_SCRIPT[@]}")
echo ${result}

Works perfectly well!!

user1361529
  • 2,667
  • 29
  • 61
0

foo.sh

#!/bin/bash
result=`./foo.py -c "$1"`
echo ${result}

foo.py

#!/usr/bin/python
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('-c',  required=True)
args,u = ap.parse_known_args()
args = vars(args)
print ("I GOT {}".format(args['c']))

Testing:

MacBook-Pro:~ ak$ sh foo.sh "Hello World"
I GOT Hello World
Alan Kavanagh
  • 9,425
  • 7
  • 41
  • 65
  • Thank you. Is there a way I can achieve this without executing the command directly? My actual script is much longer and "EXEC_SCRIPT" can have multiple values depending on different conditions. To keep the code clean, I'd like to run at the end. I'll add the clarification to my Q. – user1361529 Feb 10 '19 at 00:58