Bash – Hybrid code in shell scripts. Sharing variables

bashpythonscriptingzsh

This answer discusses how to run a multi-line Python snippet from the command line in a terminal. I noticed that the answer works great within shell scripts, even with nested indentation, which is very nice, e.g.

#!/bin/bash
some_text="Hello world"
echo $some_text

cat <<EOF | python -
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF

prints:

0 
hello
hello
1
hello
hello
2
hello
hello

However, I am having a hard time sharing variables between the shell script and the Python snippet.

  1. How can I collect the output of the python subscript in the bash script? (e.g. in a variable such as $output).

  2. How can I pass a bash variable (e.g. $some_text) to the Python script?

Best Answer

Getting a variable to Python

Since (when the EOF marker is not quoted) variable substitution occurs before text is passed from the heredoc to python's standard input, you can throw the variable right in the script.

python - <<EOF
some_text = "$some_text"
EOF

If some_text was test, python would see some_text = "test". Note however that it can be seen as a code injection vulnerability. If some_text was "; import os; os.system("evil-command"); x = ", for instance, python would see:

some_text = ""; import os; os.system("evil-command"); x = ""

and run that evil command.

If you want to be able to pull your Python code right into a script without any modifications, you could export your variable.

export some_text

and use os.environ to retrieve it.

some_text = os.environ['some_text']

That's a much saner/safer approach.


Getting output from Python

You can use command substitution to collect the script's output.

output=$(
python - <<EOF
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)

(note that all trailing newline characters are removed)

Related Question