Hybrid code in shell scripts. Sharing variables

19,269

Solution 1

The problem with your approach is that the embedded python script no longer has access to the original stdin (since its stdin is... itself).

If that's an issue you can write:

python -c '
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
'

Or if the python script may contain single quotes:

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

Or:

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

Solution 2

Use a dash as the filename:

ruby - a b <<'END'
puts ARGV.join(",")
END

python - a b <<'END'
import sys
print ",".join(sys.argv[1:])
END

I don't know if sys.argv[1:] is the right way to do this in Python. For -e / -c you can specify end of arguments with --:

set -- -a -b -c
ruby -e 'puts ARGV.join(",")' -- "$@"
python -c 'import sys; print ",".join(sys.argv[2:])' -- "$@"

Capturing output and redirecting STDERR:

x=$(ruby <<'END' 2> /dev/null
puts "a"
abort "b"
END
)

Solution 3

1) You could write variable assignments to a file in python, and then source that in your bash script.

2) Since your word (EOF) is not quoted, all the lines of the here-document are subjected to parameter expansion. You can use this to pass stuff to the python script.

Share:
19,269

Related videos on Youtube

Amelio Vazquez-Reina
Author by

Amelio Vazquez-Reina

I'm passionate about people, technology and research. Some of my favorite quotes: "Far better an approximate answer to the right question than an exact answer to the wrong question" -- J. Tukey, 1962. "Your title makes you a manager, your people make you a leader" -- Donna Dubinsky, quoted in "Trillion Dollar Coach", 2019.

Updated on September 18, 2022

Comments

  • Amelio Vazquez-Reina
    Amelio Vazquez-Reina over 1 year

    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?

    • Admin
      Admin about 11 years
      You can do python - <<EOF instead.
    • Admin
      Admin about 11 years
      jftr imo this is bad style and you should try to avoid that
    • Admin
      Admin about 11 years
      Why the /zsh tag?
  • Admin
    Admin about 11 years
    This looks great. When you said "You could also pass the variable to the script as an argument", how would you do that considering that the Python script is embedded within the shell script?
  • Admin
    Admin about 11 years
    Sorry, I forgot about that for a second. I have removed that solution and added a better solution.
  • Admin
    Admin over 8 years
    Be careful how you name the heredoc marker. When it's unquoted as in your example, shell expansion will occur within the heredoc string. For example, if your python code consisted of just print '$SHELL', the output would be /bin/bash or whatever your shell happens to be. If you change the first line to python - <<'END', the output would be $SHELL. If you are copying and pasting existing code to embed into a bash script, you almost certainly don't want shell substitutions -- you want the code to run the same way it does when it's not embedded in a bash script, right?