如何从Python脚本在后台执行shell脚本

时间:2021-08-09 04:40:11

I am working on executing the shell script from Python and so far it is working fine. But I am stuck on one thing.

我正在从Python执行shell脚本,到目前为止它工作正常。但我坚持一件事。

In my Unix machine I am executing one command in the background by using & like this. This command will start my app server -

在我的Unix机器上,我正在使用&像这样在后台执行一个命令。此命令将启动我的应用服务器 -

david@machineA:/opt/kml$ /opt/kml/bin/kml_http --config=/opt/kml/config/httpd.conf.dev &

Now I need to execute the same thing from my Python script but as soon as it execute my command it never goes to else block and never prints out execute_steps::Successful, it just hangs over there.

现在我需要从我的Python脚本执行相同的操作但是一旦它执行我的命令它就永远不会进入else块并且永远不会打印出execute_steps :: Successful,它只是挂在那里。

proc = subprocess.Popen("/opt/kml/bin/kml_http --config=/opt/kml/config/httpd.conf.dev &", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/bin/bash')
if proc.returncode != 0:
    logger.error("execute_steps::Errors while executing the shell script: %s" % stderr)
    sleep(0.05) # delay for 50 ms
else:
    logger.info("execute_steps::Successful: %s" % stdout)

Anything wrong I am doing here? I want to print out execute_steps::Successful after executing the shell script in the background.

我在这里做错了什么?我想在后台执行shell脚本后打印出execute_steps :: Successful。

All other command works fine but only the command which I am trying to run in background doesn't work fine.

所有其他命令工作正常,但只有我尝试在后台运行的命令不能正常工作。

2 个解决方案

#1


1  

There's a couple things going on here.

这里有几件事情要发生。

First, you're launching a shell in the background, and then telling that shell to run the program in the background. I don't know why you think you need both, but let's ignore that for now. In fact, by adding executable='/bin/bash' on top of shell=True, you're actually trying to run a shell to run a shell to run the program in the background, although that doesn't actually quite work.*

首先,您在后台启动一个shell,然后告诉该shell在后台运行该程序。我不知道为什么你认为你需要两者,但现在让我们忽略它。事实上,通过在shell = True之上添加executable ='/ bin / bash',你实际上是在尝试运行shell来运行shell以在后台运行程序,尽管这实际上并不起作用。 *

Second, you're using PIPE for the process's output and error, but then not reading them. This can cause the child to deadlock. If you don't want the output, use DEVNULL, not PIPE. If you want the output to process yourself, use proc.communicate().**, or use a higher-level function like check_output. If you just want it to intermingle with your own output, just leave those arguments off.

其次,您正在使用PIPE来处理流程的输出和错误,但之后却没有读取它们。这可能会导致孩子陷入僵局。如果您不想输出,请使用DEVNULL,而不是PIPE。如果您希望输出自己处理,请使用proc.communicate()。**,或使用更高级别的函数,如check_output。如果您只是希望它与您自己的输出混合,只需关闭这些参数即可。

* If you're using the shell because kml_http is a non-executable script that has to be run by /bin/bash, then don't use shell=True for that, or executable, just make make /bin/bash the first argument in the command line, and /opt/kml/bin/kml_http the second. But this doesn't seem likely; why would you install something non-executable into a bin directory?

*如果你正在使用shell因为kml_http是一个必须由/ bin / bash运行的非可执行脚本,那么不要使用shell = True或者可执行文件,只需先make make / bin / bash命令行中的参数,和/ opt / kml / bin / kml_http第二个。但这似乎不太可能;为什么要在bin目录中安装非可执行文件?

** Or you can read it explicitly from proc.stdout and proc.stderr, but that gets more complicated.

**或者您可以从proc.stdout和proc.stderr中明确地读取它,但这会变得更复杂。


At any rate, the whole point of executing something in the background is that it keeps running in the background, and your script keeps running in the foreground. So, you're checking its returncode before it's finished, and then moving on to whatever's next in your code, and never coming back again.

无论如何,在后台执行某些操作的重点是它始终在后台运行,并且您的脚本在前台运行。所以,你在它完成之前检查它的返回码,然后转到代码中的下一个,并且永远不会再回来。


It seems like you want to wait for it to be finished. In that case, don't run it in the background—use proc.wait, or just use subprocess.call() instead of creating a Popen object. And don't use & either, of course. While we're at it, don't use the shell, either:

好像你想等待它完成。在这种情况下,不要在后台使用proc.wait中运行它,或者只使用subprocess.call()而不是创建Popen对象。当然,也不要使用&。虽然我们在这里,但是不要使用shell:

retcode = subprocess.call(["/opt/kml/bin/kml_http",
                           "--config=/opt/kml/config/httpd.conf.dev"],
                          stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if retcode != 0:
    # etc.

Now, you won't get to that if statement until kml_http finishes running.

现在,在kml_http完成运行之前,你不会得到if语句。


If you want to wait for it to be finished, but at the same time keep doing other stuff, then you're trying to do two things at once in your program, which means you need a thread to do the waiting:

如果你想等待它完成,但同时继续做其他的事情,那么你试图在你的程序中一次做两件事,这意味着你需要一个线程来做等待:

def run_kml_http():
    retcode = subprocess.call(["/opt/kml/bin/kml_http",
                               "--config=/opt/kml/config/httpd.conf.dev"],
                              stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    if retcode != 0:
        # etc.

t = threading.Thread(target=run_kml_http)
t.start()
# Now you can do other stuff in the main thread, and the background thread will
# wait around until kml_http is finished and execute the `if` statement whenever
# that happens

#2


0  

You're using stderr=PIPE, stdout=PIPE which means that rather than letting the stdin and stdout of the child process be forwarded to the current process' standard output and error streams, they are being redirected to a pipe which you must read from in your python process (via proc.stdout and proc.stderr.

你正在使用stderr = PIPE,stdout = PIPE这意味着不是让子进程的stdin和stdout转发到当前进程的标准输出和错误流,而是将它们重定向到你必须从中读取的管道在你的python进程中(通过proc.stdout和proc.stderr。

To "background" a process, simply omit the usage of PIPE:

要“背景”一个过程,只需省略PIPE的用法:

#!/usr/bin/python
from subprocess import Popen
from time import sleep

proc = Popen(
    ['/bin/bash', '-c', 'for i in {0..10}; do echo "BASH: $i"; sleep 1; done'])

for x in range(10):
    print "PYTHON: {0}".format(x)
    sleep(1)

proc.wait()

which will show the process being "backgrounded".

#1


1  

There's a couple things going on here.

这里有几件事情要发生。

First, you're launching a shell in the background, and then telling that shell to run the program in the background. I don't know why you think you need both, but let's ignore that for now. In fact, by adding executable='/bin/bash' on top of shell=True, you're actually trying to run a shell to run a shell to run the program in the background, although that doesn't actually quite work.*

首先,您在后台启动一个shell,然后告诉该shell在后台运行该程序。我不知道为什么你认为你需要两者,但现在让我们忽略它。事实上,通过在shell = True之上添加executable ='/ bin / bash',你实际上是在尝试运行shell来运行shell以在后台运行程序,尽管这实际上并不起作用。 *

Second, you're using PIPE for the process's output and error, but then not reading them. This can cause the child to deadlock. If you don't want the output, use DEVNULL, not PIPE. If you want the output to process yourself, use proc.communicate().**, or use a higher-level function like check_output. If you just want it to intermingle with your own output, just leave those arguments off.

其次,您正在使用PIPE来处理流程的输出和错误,但之后却没有读取它们。这可能会导致孩子陷入僵局。如果您不想输出,请使用DEVNULL,而不是PIPE。如果您希望输出自己处理,请使用proc.communicate()。**,或使用更高级别的函数,如check_output。如果您只是希望它与您自己的输出混合,只需关闭这些参数即可。

* If you're using the shell because kml_http is a non-executable script that has to be run by /bin/bash, then don't use shell=True for that, or executable, just make make /bin/bash the first argument in the command line, and /opt/kml/bin/kml_http the second. But this doesn't seem likely; why would you install something non-executable into a bin directory?

*如果你正在使用shell因为kml_http是一个必须由/ bin / bash运行的非可执行脚本,那么不要使用shell = True或者可执行文件,只需先make make / bin / bash命令行中的参数,和/ opt / kml / bin / kml_http第二个。但这似乎不太可能;为什么要在bin目录中安装非可执行文件?

** Or you can read it explicitly from proc.stdout and proc.stderr, but that gets more complicated.

**或者您可以从proc.stdout和proc.stderr中明确地读取它,但这会变得更复杂。


At any rate, the whole point of executing something in the background is that it keeps running in the background, and your script keeps running in the foreground. So, you're checking its returncode before it's finished, and then moving on to whatever's next in your code, and never coming back again.

无论如何,在后台执行某些操作的重点是它始终在后台运行,并且您的脚本在前台运行。所以,你在它完成之前检查它的返回码,然后转到代码中的下一个,并且永远不会再回来。


It seems like you want to wait for it to be finished. In that case, don't run it in the background—use proc.wait, or just use subprocess.call() instead of creating a Popen object. And don't use & either, of course. While we're at it, don't use the shell, either:

好像你想等待它完成。在这种情况下,不要在后台使用proc.wait中运行它,或者只使用subprocess.call()而不是创建Popen对象。当然,也不要使用&。虽然我们在这里,但是不要使用shell:

retcode = subprocess.call(["/opt/kml/bin/kml_http",
                           "--config=/opt/kml/config/httpd.conf.dev"],
                          stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if retcode != 0:
    # etc.

Now, you won't get to that if statement until kml_http finishes running.

现在,在kml_http完成运行之前,你不会得到if语句。


If you want to wait for it to be finished, but at the same time keep doing other stuff, then you're trying to do two things at once in your program, which means you need a thread to do the waiting:

如果你想等待它完成,但同时继续做其他的事情,那么你试图在你的程序中一次做两件事,这意味着你需要一个线程来做等待:

def run_kml_http():
    retcode = subprocess.call(["/opt/kml/bin/kml_http",
                               "--config=/opt/kml/config/httpd.conf.dev"],
                              stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    if retcode != 0:
        # etc.

t = threading.Thread(target=run_kml_http)
t.start()
# Now you can do other stuff in the main thread, and the background thread will
# wait around until kml_http is finished and execute the `if` statement whenever
# that happens

#2


0  

You're using stderr=PIPE, stdout=PIPE which means that rather than letting the stdin and stdout of the child process be forwarded to the current process' standard output and error streams, they are being redirected to a pipe which you must read from in your python process (via proc.stdout and proc.stderr.

你正在使用stderr = PIPE,stdout = PIPE这意味着不是让子进程的stdin和stdout转发到当前进程的标准输出和错误流,而是将它们重定向到你必须从中读取的管道在你的python进程中(通过proc.stdout和proc.stderr。

To "background" a process, simply omit the usage of PIPE:

要“背景”一个过程,只需省略PIPE的用法:

#!/usr/bin/python
from subprocess import Popen
from time import sleep

proc = Popen(
    ['/bin/bash', '-c', 'for i in {0..10}; do echo "BASH: $i"; sleep 1; done'])

for x in range(10):
    print "PYTHON: {0}".format(x)
    sleep(1)

proc.wait()

which will show the process being "backgrounded".