Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
1  
You can do python - <<EOF instead. –  paraxor Apr 30 '13 at 22:06
    
jftr imo this is bad style and you should try to avoid that –  Ulrich Dangel Apr 30 '13 at 22:23
1  
Why the /zsh tag? –  Stéphane Chazelas May 1 '13 at 20:34

4 Answers 4

up vote 10 down vote accepted

Getting a variable to Python

Since 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".

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']

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
)
share|improve this answer
    
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? –  user815423426 Apr 30 '13 at 22:18
    
Sorry, I forgot about that for a second. I have removed that solution and added a better solution. –  paraxor Apr 30 '13 at 22:22

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
)
share|improve this answer

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
)
share|improve this answer

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|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.