首页 > 解决方案 > Python使用shell函数

问题描述

我在 Win 10 下使用 Msys2 便携版,但我想这同样适用于许多其他 Linux/bash。

从 shell 提示符运行时,如何让 python 脚本知道它的 shell 函数和相应的参数?

我有一个 bash shell,我在其中定义了一个 shell 函数myfun

$ type myfun
myfun is a function
myfun ()
{
    ...
    (this contains a call to a shell script, 
      that is found in a directory which is part of $PATH)
}

这可能需要参数,例如,$ myfun d我有一个 python 脚本myscript.py,当从 bash shell 运行时效果很好。现在我想添加要在其中myscript.py执行myfun的行,并让它也可以使用(与以前相同)

$ python3 myscript.py



编辑#1:收到答案后,我用export -f myfun. 然后我在我的脚本中尝试了 6 种替代方法:(使用/不使用bash -c),结合(subprocess.run、、os.systemos.popen

我在这里列出了 6 种组合中的每一种,添加到 的行myscript.py以及执行时获得的输出python3 myscript.py

  1. 添加
import subprocess
subprocess.run(["myfun", "d"])

获得(与之前相同)

Traceback (most recent call last):
...
FileNotFoundError: [Errno 2] No such file or directory: 'myfun'
  1. 添加
import os
os.system("myfun d")

得到了预期的结果。

  1. 添加
import os
stream = os.popen("myfun d")

获得 没有错误,但显然什么也没做(很奇怪,必须进一步检查发生了什么)。

  1. 添加
import subprocess
subprocess.run(["bash", "-c", "myfun", "d"])

获得:执行了函数,但忽略了函数参数。

  1. 添加
import os
os.system("bash -c \"myfun d\"")

得到了预期的结果。

  1. 添加
import os
stream = os.popen("bash -c \"myfun d\"")

获得 没有错误,但显然什么也没做(很奇怪,必须进一步检查发生了什么)。



原始问题和状态

我尝试了几个选项,都失败了(请参阅下面的详细信息)。一方面,似乎即使myscript.py从 bash shell执行,它也会在sh不知道我的 shell 函数的情况下启动一个单独的 shell。另一方面,如果添加print( os.environ )myscript.py,我会看到在 中设置的环境变量~/.bashrc,因此至少脚本从 shell 正确继承了这些变量。


我在这里列出添加到的行myscript.py,以及执行时获得的输出python3 myscript.py

  1. 添加
import subprocess
subprocess.run(["myfun", "d"])

获得

Traceback (most recent call last):
...
FileNotFoundError: [Errno 2] No such file or directory: 'myfun'
  1. 添加
import os
os.system("myfun d")

获得

sh: myfun: command not found
  1. 添加
import os
stream = os.popen("myfun d")

获得

/bin/sh: myfun: command not found

标签: pythonbashshellenvironment-variables

解决方案


默认情况下,环境是私有的。导出函数并确保它bash在系统调用中运行。myfun是一个bash函数,你必须运行bash才能运行它的函数。

$ myfun() { echo 1; }
$ export -f myfun
$ python <<<'import os; os.system("bash -c myfun")'
1
$ python <<<'import subprocess as s; s.run(["bash", "-c", "myfun"])'
1
$ python <<<'import os; os.popen("bash -c \'myfun"); # ... '

我通常将参数传递给内部 bash 命令作为脚本的参数并传递正确引用的$@扩展:

$ myfun() { echo "\$#=$#" "\$*=$*"; }
$ export -f myfun
$ python <<<'import os; os.system("bash -c \"myfun \\\"\$@\\\"\" -- d")'
$#=1 $*=d

# or way better use subprocess
$ python <<<'import subprocess as s; s.run(["bash", "-c", "myfun \"$@\"", "--", "d"])'
$#=1 $*=d

# but still can do the more unsafe version
# that will undergo word expansion or you have to properly escape the strings
# (and double-double escape for `os.systems`)
$ python <<<'import subprocess as s; s.run(["bash", "-c", "myfun d"])'
$#=1 $*=d

推荐阅读