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 <my_mail@192.168.75.1>"
to_addr = "<my_mail@192.168.75.1"
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()
GAD3R
  • 66,769

8 Answers8

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"
Dalvenjia
  • 2,026
  • 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 '17 at 09:01
  • 2
    I hate downvotes without comment. It works for me. Upvoted it ;-) – kev Mar 27 '18 at 03:47
  • 4
    Upvoted this answer because (unlike the accepted answer) it doesn't write the script to the file system. – Huw Walters Mar 28 '20 at 09: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 06 '21 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 '23 at 06: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
Ketan
  • 9,226
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(&quot;you typed&quot;, a) print(&quot;\033[1;32mbye...\033[m&quot;)"

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:", "$((67))")' An odd string: $((67))

nadapez
  • 145
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.
rysson
  • 141
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
terdon
  • 242,166
JohannRamos
  • 1,311
  • On the bash-script execution time some variables are get defined, I need this on the python script for the message body. – Amal P Ramesh Feb 13 '15 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
echo9
  • 9
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

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