嵌入bash到Python中

23

我正在编写一个Python脚本,时间有点紧。有些操作我在bash中很熟悉,所以我想知道如何将一些bash命令嵌入到Python脚本中。

谢谢


3
“有些东西”? 如果您有特定的事情需要帮助,我们可以帮助您避免嵌入bash在Python中的错误。 - S.Lott
1
@S.Lott 我知道这是很久以前的事了,但我刚刚开始涉足bash脚本编程。我已经确定,只要可能,我肯定会使用Python(这是我目前主要使用的语言)。有没有一种快速的方法来解释为什么在Python中嵌入bash是一个错误? - floer32
9个回答

34

最佳的做法:

def run_script(script, stdin=None):
    """Returns (stdout, stderr), raises error on non-zero return code"""
    import subprocess
    # Note: by using a list here (['bash', ...]) you avoid quoting issues, as the 
    # arguments are passed in exactly this order (spaces, quotes, and newlines won't
    # cause problems):
    proc = subprocess.Popen(['bash', '-c', script],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        stdin=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    if proc.returncode:
        raise ScriptException(proc.returncode, stdout, stderr, script)
    return stdout, stderr

class ScriptException(Exception):
    def __init__(self, returncode, stdout, stderr, script):
        self.returncode = returncode
        self.stdout = stdout
        self.stderr = stderr
        Exception().__init__('Error in script')

你还可以为ScriptException添加一个好的__str__方法(你肯定需要它来调试你的脚本)--但我把这个留给读者自己去完成。
如果你不使用stdout=subprocess.PIPE等参数,那么脚本将直接附加到控制台。如果你有一个来自ssh的密码提示,这真的非常方便。因此,你可能希望添加标志来控制是否捕获stdout、stderr和stdin。

2
我非常确定这比他想要的酷炫10倍。 - bukzor
1
很好!已添加到我的工具箱中。 - hauptmech
函数 run_script 中参数 stdin 的用途是什么? - ruanhao
你为什么要使用 -c - alper
选项-c告诉bash执行字符串。因此,bash "echo hi"将尝试执行名为"echo hi"的脚本文件(这是行不通的!),而bash -c "echo hi"将尝试运行"echo hi"(并显示"hi")。 - Ian Bicking

15
如果您想调用系统命令,请使用subprocess模块。

6
-1 是指使用 TinyURL 链接到谷歌搜索而不是直接链接到有用的资源。 - Donal Fellows
6
感谢您对最后附带资源的点赞,以及正确回答这个问题。以下是直接链接:http://docs.python.org/library/subprocess.html - Personman
2
@Donal,我这样做的原因是因为还有其他很有用的网站也展示了子进程的例子。此外,第一个搜索结果就是文档本身。这应该不会成为问题。 - ghostdog74

7
假设该命令被主机系统支持:
import os
os.system('command')

如果你有一个很长的命令,或者一组命令,你可以使用变量。 例如:

# this simple line will capture column five of file.log
# and then removed blanklines, and gives output in filtered_content.txt.

import os

filter = "cat file.log | awk '{print $5}'| sed '/^$/d' > filtered_content.txt"

os.system(filter)

7

Is

import os
os.system ("bash -c 'echo $0'")

你要我为你做什么?

编辑:关于易读性

当然,你可以让它更易读。

import os
script = """
echo $0
ls -l
echo done
"""
os.system("bash -c '%s'" % script)

编辑2:关于宏,据我所知,Python并没有像我知道的那样做得那么远,但是在...
import os
def sh(script):
    os.system("bash -c '%s'" % script)

sh("echo $0")
sh("ls -l")
sh("echo done")

在前面的例子中,你基本上可以得到你想要的(但你必须允许一些方言限制)。

还有,有没有什么快捷方式或解决方法可以使它更容易或更具吸引力? - Open the way
我的意思是,我能否在Python中定义一些宏,例如; (#define sh os.system("....."),因此在代码中我只需编写sh ps -ef? - Open the way

3

当bash命令简单且没有括号、逗号和引号时,子进程(subprocess)和os.system()运行良好。嵌入复杂的bash参数的简单方法是在Python脚本末尾添加bash脚本,并使用带有唯一字符串注释的简单os.system()命令来tail并转换为bash文件。

#!/usr/bin/python
## name this file  "file.py"
import os
def get_xred(xx,yy,zz):
    xred=[]
####gaur###
    xred.append([     zz[9] ,  zz[19] ,  zz[29]     ])
    xred.append([     zz[9] ,  xx[9]  ,  yy[9]      ])
    xred.append([     zz[10],  zz[20] ,  zz[30]     ])
    xred.append([     zz[10],  xx[10] ,  yy[10]     ])
###nitai###
    xred=np.array(xred)
    return xred
## following 3 lines executes last 6 lines of this file.
os.system("tail -n 6 file.py >tmpfile1")
os.system("sed 's/###123//g' tmpfile1>tmpfile2")
os.system("bash tmpfile2")
###### Here ###123 is a unique string to be removed
###123#!/bin/sh
###123awk '/###gaur/{flag=1;next}/###nitai/{flag=0} flag{print}' file.py >tmp1
###123cat tmp1 | awk '{gsub("xred.append\\(\\[","");gsub("\\]\\)","");print}' >tmp2
###123awk 'NF >0' tmp2 > tmp3
###123sed '$d' tmp3 |sed '$d' | sed '$d' >rotation ; rm tmp*

2

我创建了Sultan来解决你正在尝试做的事情。它没有任何外部依赖,并且尽可能地轻巧,并提供一个Pythonic接口到Bash。

https://github.com/aeroxis/sultan


Sultan看起来很酷,我打算使用它;如果这个工具看起来真正有用/适用并且是开源的,我不认为人们应该仅仅因为不喜欢任何可能看起来自我推销的东西而对其进行贬低。这个工具看起来很好!答案是合理的。 - Max von Hippel

1

@Ian Bicking的回答很有用,但它只允许我们运行脚本。相反,我们可以想出一种更灵活的代码,可以运行命令。我有一个不同的方法。

#!/usr/bin/env python3

from subprocess import Popen, PIPE


class BashCommandsException(Exception):
    def __init__(self, returncode, output, error_msg):
        self.returncode = returncode
        self.output = output
        self.error_msg = error_msg
        Exception.__init__('Error in executed command')


def popen_communicate(cmd, stdout_file=None):
    """Acts similir to lib.run(cmd) but also returns the output message captures on
    during the run stdout_file is not None in case of nohup process writes its
    results into a file
    """
    cmd = list(map(str, cmd))  # all items should be string
    if stdout_file is None:
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
    else:
        with open(stdout_file, "w") as outfile:
            # output written into file, error will be returned
            p = Popen(cmd, stdout=outfile, stderr=PIPE, universal_newlines=True)
            output, error = p.communicate()
            p.wait()
            return p, output, error

    output, error = p.communicate()
    output = output.strip().decode("utf-8")
    error = error.decode("utf-8")
    return p, output, error


def run(cmd):
    log_file = "/tmp/log.txt"
    # if log_file is not provided returned output will be stored in output
    p, output, error_msg = popen_communicate(cmd, log_file)
    if p.returncode != 0:
        raise BashCommandsException(p.returncode, output, error_msg, str(cmd))
    return output


if __name__ == "__main__":
    # This could be any command you want to execute as you were in bash
    cmd = ["bash", "script_to_run.sh"]
    try:
        run(cmd)
    except Exception as e:
        print(e)


0

@gander 我认为你被踩是因为Commands正在从Python中弃用。 - David
1
哦!不是一个好的开始。但对于那些被困在旧版本上的人来说,这仍然是有效的吧? - agander

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接