21

I need to include below python script inside a bash script.

If the bash script end success, I need to execute the below script:

#!/usr/bin/python    
from smtplib import SMTP
import datetime
debuglevel = 0

smtp = SMTP()
smtp.set_debuglevel(debuglevel)
smtp.connect('192.168.75.1', 25)
smtp.login('my_mail', 'mail_passwd')

from_addr = "My Name <[email protected]>"
to_addr = "<[email protected]"
subj = "Process completed"
date = datetime.datetime.now().strftime( "%d/%m/%Y %H:%M" )
#print (date)
message_text = "Hai..\n\nThe process completed."

msg = "From: %s\nTo: %s\nSubject: %s\nDate: %s\n\n%s" % ( from_addr, to_addr, subj, date, message_text )

smtp.sendmail(from_addr, to_addr, msg)
smtp.quit()
4
  • 4
    script.sh && python script.py ?
    – Costas
    Feb 13, 2015 at 18:04
  • 2
    Why "include" it? Why not just run it?
    – terdon
    Feb 13, 2015 at 18:10
  • 1
    To call bash-script from python import os os.system ("./script.sh")
    – Costas
    Feb 13, 2015 at 18:17
  • python -c $(cat << EOF ... ... EOF )" More in my answer
    – nadapez
    Oct 6, 2021 at 19:40

8 Answers 8

43

Just pass a HereDoc to python -.

From python help python -h:

- : program read from stdin

#!/bin/bash

MYSTRING="Do something in bash"
echo $MYSTRING

python - << EOF
myPyString = "Do something on python"
print myPyString

EOF

echo "Back to bash"
5
  • 1
    I don't see why this was downvoted. It's a simple and workable solution for some cases. It does have the (major) limitation that you can't use standard input in the python script, though (since it's receiving stdin from the heredoc).
    – pyrocrasty
    Feb 11, 2017 at 9:01
  • 2
    I hate downvotes without comment. It works for me. Upvoted it ;-)
    – kev
    Mar 27, 2018 at 3:47
  • 4
    Upvoted this answer because (unlike the accepted answer) it doesn't write the script to the file system. Mar 28, 2020 at 9:16
  • 1
    look at my answer. I used the -c argument, not -. Note also that this is not very efficient, because the program is read and compiled line by line as it runs
    – nadapez
    Oct 6, 2021 at 19:34
  • @HuwWalters For older bash relesaes, here-documents are saved to a temporary file. For newer releases after 5.1 ("bash-5.2-rc1" or newer (2022)), if the compatibility level of the shell is 50 or lower, or if the size of the here-document is larger than the pipe buffer size of the system, the here-document is saved to a temporary file.
    – Kusalananda
    Jan 15 at 6:26
22

You can use heredoc if you want to keep the source of both bash and python scripts together. For example, say the following are the contents of a file called pyinbash.sh:

#!/bin/bash

echo "Executing a bash statement"
export bashvar=100

cat << EOF > pyscript.py
#!/usr/bin/python
import subprocess

print 'Hello python'
subprocess.call(["echo","$bashvar"])

EOF

chmod 755 pyscript.py

./pyscript.py

Now running pyinbash.sh will yield:

$ chmod 755 pyinbash.sh
$ ./pyinbash.sh
Executing a bash statement
Hello python
100
4
  • Per OP's comment to another answer, I updated my answer which takes care of bash variables in the python script. Feb 13, 2015 at 18:32
  • 1
    I can't get the variable's value from bash-script into python script. Mar 9, 2015 at 10:29
  • I replace subprocess.call(["echo","\$bashvar"]) into subprocess.call(["echo","bashvar"]) now it's working. Mar 9, 2015 at 10:35
  • That was pretty neat! Jan 13, 2016 at 8:19
5

As shown (but not explained) in a couple of other answers, and as documented in the Python 3.11.1 documentation Command line and environment, you can use -c command:

-c command

    Execute the Python code in command.  command can be one or more statements separated by newlines, with significant leading whitespace as in normal module code.

In other words, you can put your entire Python script into a Bash string.  Here’s a slightly complicated / convoluted approach, using command substitution and a here document:

#!/bin/bash
python3 -c "$(cat << EOF

a = input('?>')
print('you typed', a)
print('\033[1;32mbye...\033[m')

EOF
)"            

This works.  The $() (command substitution) passes the output of the command inside (in this case cat) as the argument to Python.  There is no pipelining so standard input can be used in the Python code.

This simpler approach (making the Python script a literal string) also works:

#!/bin/bash

python3 -c "
a = input('?>') 
print('you typed', a)
print('\033[1;32mbye...\033[m')"

This has the usual issue with double-quoted strings in Bash: shell meta-characters ", $, ` and \ need to be escaped.  For example, if you need to use " in your Python code, you should escape it like this:

#!/bin/bash

python3 -c "
a = input('?>') 
print(\"you typed\", a)
print(\"\033[1;32mbye...\033[m\")"

But why not just change all the single quotes in your Python code to double quotes, and put the entire Python script into single quotes?

#!/bin/bash

python3 -c '
a = input("?>")
print("you typed", a)
print("\033[1;32mbye...\033[m")'

Similarly,

$ python3 -c "print('An odd string:', '$((6*7))')"
An odd string: 42

$ python3 -c 'print("An odd string:", "$((6*7))")'
An odd string: $((6*7))
4

This is old question, but maybe useful for someone. It's a way to include Python script inside a Bash script and use sys.stdin.

Extract Python script and run it with -c. The trick is to use a function, that allows use ' and " in the script. Also sys.stdin is available.

#!/bin/bash

read_file()
{
  local name="${1//./[.]}"  # escape name for sed regex
  sed -En '/^#---=== '"$name"' ===---$/,$ {/^#---=== '"$name"' ===---$/ n; /^#---===/ q; p; }' "$0"
}

echo Johny | python3 -c "$(read_file script.py)"
exit

#---=== script.py ===---
import sys
print('Your name is', sys.stdin.readline().strip())
#---===---

Explanation

Bash parse and execute a script line by line (command by command). It allows to put anything after exit, even binary data.

There is a file-system section in our case there. Line starting with #---=== are detected by the sed regular expression. The file content between the lines are printed out... and used as a script in Python with -c.

It's possible to put many files between #---=== patterns.

Details
  1. Python execute code from a -c argument.
  2. There is shell substitution used, $() executes command and put its stdout as a text.
  3. The read_file function takes a filename as the first argument.
  4. ${//} replaces all dots in the filename and next the replaced filename is put into local variable name
  5. Dot (.) means any character in a regular expression, [] is a list od characters, than [.] means a dot literally.
  6. sed is a stream text editor, there is used:
    • -E – use extended regular expressions in the script
    • -n – suppress automatic printing of pattern space
  7. Sed operates on the same script file $0, it's way we can use only one file.
  8. Sed takes lines by range command (PAT,PAT)
    • starting from regex /…/ (#---=== with the filename and ===---)
    • to the end of the script $
    • note: whole script command is quoted, but an apostrophe ' suppress $VAR substitution, then there are more quotations '...'"$name"'...'.
  9. Next are set of command to work on the lines range {…}, separated by semicolon ;
  10. First line from the lines range is skipped (n like next), there is #---=== FILENAME ===--- line. The same pattern is used in //.
  11. If there is another #---=== line (//) it means the next file section in the script, than the command q (lik quit) is used. This trick allows as to use end of file ($) in the line range pattern.
  12. Just print a line with command p. All printed lines are after #---=== FILENAME ===--- and before next #---===
  13. Ale printed lines from sed command are executed in the Python.
2
3

The simplest approach is to just save the python script as, for example script.py and then either call it from the bash script, or call it after the bash script:

#!/usr/bin/env bash
echo "This is the bash script" &&
/path/to/script.py

Or

script.sh && script.py
1
  • On the bash-script execution time some variables are get defined, I need this on the python script for the message body. Feb 13, 2015 at 18:23
0

How about this for an example:

PYTHON_BIN=/usr/bin/python
if [ -x $PYTHON_BIN ]; then
$PYTHON_BIN -c "print 'Hello, world'"
else
echo 'Hello, world'
fi

VS

$ ./foobar.py
env: python: No such file or directory
2
0

I know this post is old, but I thought that I would share my code that is a working example.

#!/bin/bash
printf "This is BASH\n"
printf "Please enter some text: "; read ans
export ans

cat << EOF > pyscript.py
#!/usr/bin/python3 -tt
import subprocess

print('............This is Python')
subprocess.call(["echo","............$ans"])
print('............Done with Python')

EOF

chmod 770 pyscript.py

./pyscript.py

printf "This is BASH again\n"
exit 0
1
0

Here's a funnier way of doing it:

#!/bin/bash

"""" 2>/dev/null
# BASH CODE STARTS HERE

echo "Hello world from Bash!"

# BASH CODE ENDS HERE

python3 $0
exit
"""

# PYTHON CODE STARTS HERE

print("Hello world from Python!")

# PYTHON CODE ENDS HERE

Save this to a file, chmod +x it and execute it:

localhost:~# ./pysh
Hello world from Bash!
Hello world from Python!

An "explanation"

This silly script is actually a polyglot: it means something in two different programming languages (in our case, shell scripting and Python).

By making Python think the Bash section of the script is just a docstring literal, and by deviously tricking the shell into stopping execution before the "meaningless" (Python) part of the script is reached, we can have a file that works both as a Bash script and as a Python program.

(The 2>/dev/null part redirects STDERR from the """" command to the null device, thus preventing Bash from displaying an error)

If you need a more complex bash script (you need to actually do stuff in Bash after executing the Python code), you can define functions above and invoke them before the exit line. Enjoy :P

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .