Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use functions defined in the shell from python?

Example:

#!/bin/bash

function my_test(){
    echo this is a test $1
}

my_test 1

python -c "from subprocess import check_output; print(check_output('my_test 2', shell=True))"

output:

this is a test 1
/bin/sh: my_test: command not found
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.5/subprocess.py", line 629, in check_output
    **kwargs).stdout
  File "/usr/lib/python3.5/subprocess.py", line 711, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'my_test 2' returned non-zero exit status 127
like image 794
orestisf Avatar asked Jan 01 '16 13:01

orestisf


2 Answers

You need to export the shell function, so it will be inherited by child shells.

#!/bin/bash

function my_test(){
    echo this is a test $1
}

my_test 1

export -f my_test
python -c "from subprocess import check_output; print(check_output('my_test 2', shell=True))"
like image 198
Barmar Avatar answered Nov 01 '22 05:11

Barmar


Exporting a function, which is not supported by all shells, places the code into the environment block. This is a language-neutral block of text, which is copied from parent to child when a new process is created.

This was the basis of ShellShock.

The trouble is that the two languages Bash and Python are totally different, so a function written in one will not be directly executable by the other without translation. The child process could scan the environment block looking for the function, then translate and compile it into its own language. A lot of work, and could easily be a security issue.

If you just want to go Bash->Python->Bash, then exporting the function from first Bash script should do it, because it will be copied into each environment block. However you also state in a comment that you don't want the first script to export it.

Well you could read the function code using python into a text string, then put it into the environment block yourself (this is what export does in the shell). Use the os.environ dictionary.

The actual name used is dependant on your version of Bash. The ShellShock vulnerability resulted in a lot of changes. Best create a test function, export it, then use env to find its full name. For example on my version a simple function called gash appears in the environment block as BASH_FUNC_gash%%.

BASH_FUNC_gash%%=() {  echo 'Hollow world'
}

For example:

import os
import subprocess

fun_body="""() {  echo 'Hollow world'
}
"""
os.environ['BASH_FUNC_gash%%'] = fun_body

p1 = subprocess.Popen('./myscript.sh')

p1.wait()

The script (myscript.sh) contains:

#!/bin/bash
gash

Alternatively you could look at your design again. Mixing languages is always problematic, why not write the whole lot in Python?

like image 26
cdarke Avatar answered Nov 01 '22 05:11

cdarke