1

I have an array HSPACE which is defined inside a python script. I am trying to pass this array to a shell script "arraytest" and execute it from python itself. I am trying with the following piece of code but it doesnt seem to be working:

HSPACE=[0.01, 0.009, 0.008, 0.007]
subprocess.call(["./arraytest"], HSPACE, shell=True)

The content of the shell script is:

#!/bin/bash
for i in ${HSPACE[@]}
do
echo $i
done
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
Aspro
  • 45
  • 2
  • 7
  • 3
    Don't use `shell=True` unless you have a compelling reason and know exactly *why* you're using it; it adds substantial complexity and security exposure. – Charles Duffy May 25 '16 at 20:24
  • ...also, `${HSPACE[@]}` -- while not useful here because HSPACE isn't inherited (arrays *can't* be exported through the environment, so it's not just a matter of syntax but feasibility) -- is incorrect syntax to expand an array's contents while keeping the original division between element boundaries; it behaves precisely identically to `${HSPACE[*]}`, which combines elements of the array with the first character of `IFS` and then subjects the result to string-splitting and globbing. To avoid this, you need to quote: `"${HSPACE[@]}"`. – Charles Duffy May 25 '16 at 20:37
  • (...well, I say "can't". Not natively -- you can generate a string which `eval`s to array contents, but the shell receiving and evaluating it needs to trust the source of that value, making it a poor practice and prone to creating shell injection vulnerabilities if not done properly). – Charles Duffy May 25 '16 at 20:37
  • @CharlesDuffy The solution below works fine. If we are to send multiple variables from python to the shell script, is it possible to proceed in the same way? – Aspro May 25 '16 at 23:16
  • You can pass multiple arrays by prefixing each with the length -- or putting an array-specific sigil before each element. For instance, `H:0.01 H:0.009 H:0.008 H:0.007 V:1.23 V:4.56` is an example of what the latter argv would look like; the shell script could then just iterate over `"$@"` and assign each item to the correct array based on its prefix. – Charles Duffy May 26 '16 at 01:44
  • ...also, you might look at the answer to http://stackoverflow.com/questions/9898939/handling-long-edit-lists-in-xmlstarlet for an example of the bash parts of the former (length-prefix) approach for passing arrays. – Charles Duffy May 26 '16 at 01:48

1 Answers1

4

The easy answer is to pass each array entry as a literal argv entry:

subprocess.call(['./arraytest'] + [str(s) for s in HSPACE], shell=False)

...thereafter...

#!/bin/bash
printf 'hspace array entry: %q\n' "$@"

Another approach is to pass an array as a NUL-delimited stream on stdin:

p = subprocess.Popen(['./arraytest'], shell=False, stdin=subprocess.PIPE)
p.communicate('\0'.join(str(n) for n in HSPACE) + '\0')

...and, in your shell:

#!/bin/bash
arr=( )
while IFS= read -r -d '' entry; do
  arr+=( "$entry" )
done

printf 'hspace array entry: %q\n' "${arr[@]}"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • This is a good answer, I guess the only think you didn't mention is the ability to set environment variables for the subprocess. Of cause setting complex variables as arrays are not possible. But might be worth mentioning anyway. – Andreas Louv May 25 '16 at 21:38
  • @andlrc, *nod* -- I mentioned that in a comment in the question, but since passing an arbitrary array that way requires `eval` to unpack it, I'm hesitant to put that code in an answer and thereby endorse its use. (Though one could also, say, base64-encode a NUL-delimited stream in the environment, but that's just getting silly). – Charles Duffy May 25 '16 at 21:41