Skip to content

Instantly share code, notes, and snippets.

@Jwink3101
Created April 27, 2018 16:53
Show Gist options
  • Save Jwink3101/728605b43ca55f7d3be16d810b16af9b to your computer and use it in GitHub Desktop.
Save Jwink3101/728605b43ca55f7d3be16d810b16af9b to your computer and use it in GitHub Desktop.
Two examples of running python inside a bash script. The first is a simple one that just returns a result while the second gives bash a series of commands to run or a result to print (such as from parsing command line arguments).
#!/usr/bin/env bash
############################################################################
################################## Intro ###################################
############################################################################
# Example on how to run python inside of bash. Anything that is printed
# in the python part is "returned" to bash
#
# Examples:
# Simple: Just return the output
# Complex: Tell bash which commands to run and/or print results from
# parsing the command-line arguments
#
# Why:
# This is useful when you want to be able to 'source script.sh' and/or
# when you need to be able to use certain environment variables and paths
# such as bash environment modules.
#
# For many uses, python subprocess is a *MUCH* better solution
#
# Gotchas:
# There are a few things to know about writing these combined scripts:
# * Bash variables will be expanded! This can be useful, but may
# also cause problems
# * Certain characters, even when in python string, must be escped or
# avoided. The most notable example is the backtick "`"
# * sys.argv in python is what is passed! Not the one for the script.
# See the second invocation for "Simple Example"
############################################################################
############################## Simple Example ##############################
############################################################################
# This example is just to do some minor processing.
adder(){
python - "$@" <<ENDPY
import sys
tot = 0.0
for arg in sys.argv[1:]:
tot = tot + float(arg)
# return
print(tot)
ENDPY
}
# These are commented out for the complex example below.
#echo $(adder 1 2 3.4) # Add specific arguments
#echo $(adder "$@") # Add the arguments from the command line
############################################################################
############################# Complex Example ##############################
############################################################################
# This example demonstrates some more advanced usage. This is, of course, a
# toy problem but it demonstrates how to use python do build a complex
# command. As noted in the Intro, this is useful when you want to execute
# a series of bash commands in the primary bash environment (and potentially
# with a "source myscript.sh" invocation)
# Remember that variables are filled in
VERSION='20180427'
PROG='adder-example'
# This is used as a sentinel to later execute vs just echo. This is NOT the
# main example
execflag=$(python -c "import random;print(''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(50)))")
adder_cli() {
python - "$@" <<ENDPY
from __future__ import division, print_function, unicode_literals
__version__ = $VERSION
import sys
import argparse
sys.argv[0] = "$PROG" # Force the name
description="""
Add (or subtract if set) command line arguments. Print them to the screen
and then *also* write to a file. (recall this is a toy example)
"""
parser = argparse.ArgumentParser(description=description,epilog='version: $VERSION')
parser.add_argument('--neg',action='store_true',help='Negate the sum')
parser.add_argument('num',nargs='+',help='number to add')
args = parser.parse_args(sys.argv[1:])
# Send in the execflag so everything else gets evaluated by bash
print('${execflag}')
tot = sum(float(a) for a in args.num)
if args.neg: tot = -1.0*tot
bashcmd = []
bashcmd.append('echo "The sum in: {}"'.format(tot))
bashcmd.append('echo "The sum in: {}" >> exampleout'.format(tot))
for cmd in bashcmd:
print(cmd)
ENDPY
}
# This part calls the python code and then either executes it or
# just prints the result
cmd=$(adder_cli "$@")
if [[ "$cmd" == ${execflag}* ]]; then
# Means we run it
IFS= # Makes it not break at new lines
cmd="$(echo $cmd | sed "s/${execflag}//g")"
IFS=$'\n'
for line in $cmd; do
echo "[$PROG] executing: \$ ${line}"
eval "$line"
done
else
echo "$cmd"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment