Python:在后台运行带有重定向的bash命令并获取进程ID

时间:2017-05-22 07:28:25

标签: python linux bash subprocess

我正在Python 2.6.6平台使用Centos 6

我需要在后台运行Python的bash命令,其中包含重定向操作符,需要从子进程对象中读取后台进程的pid。

我尝试了以下代码段,但它无效。

我的代码

import subprocess

# Below code throws child_exception
myProcess = subprocess.Popen('tail -f -n 0 /home/user123/mainFile.txt >> /home/user123/tailFile.txt &', subprocess.PIPE)

#If I use the below command, terminating the process kills
#only the shell process and leaves the tail process as orphan
myProcess = subprocess.Popen('tail -f -n 0 /home/user123/mainFile.txt >> /home/user123/tailFile.txt', shell=True, subprocess.PIPE)

cmd = ['tail', '-f', '-n', '0', '/home/user123/mainFile.txt', '>>', '/home/user123/tailFile.txt', '&']
#Below line throws bash error saying: "cannot open file '>>'"
myProcess = subprocess.Popen(cmd, stdout=subprocess.PIPE)

myProcessPid = myProcess.communicate()[0]

最后,我需要获得正在后台运行的尾部进程的pid。

2 个答案:

答案 0 :(得分:2)

在python中包装纯shell是没有用的或明智的。

subprocess.Popen对象有自行执行重定向和类似事务的方法,而不是依赖shell。这是一个例子。

 import subprocess
 with open("ls.output.txt", "w") as f:
     # This is the way you'd run "ls -al > ls.output.txt" in the background
     p = subprocess.Popen(["ls", "-al"], stdout=f) # This will run in the background

 p.wait() # Wait till process completes

 with open("ls.output.txt") as f:
        print (f.readline()) # Will print first line of ls -al output (total 2657828)

答案 1 :(得分:0)

除非将 shell = True 传递给 subprocess.Popen()调用,否则不会执行这些重定向。更好的方法是使用 stdout = subprocess.PIPE 选项并自己捕获输出。

shell通常会为您重定向。此命令将命令分解为命令和参数的向量(列表)是针对命令的,因为shell会将这些命令传递给 execve()系统调用。 shell重定向,管道和其他操作符不属于此。

你也不需要&运算符作为 subprocess.Popen()进程在后台自动运行。它可能在I / O上被阻止,您可以从 suprocess.PIPE 中轮询和读取它。

更重要的是,您根本不必使用运行 tail 命令的子进程。如果您只想跟随文件末尾,可以使用 file.seek() file.tell() file.readline( ) os.fstat()方法。

这是一个直接在Python中实现 tail -f 语义的简单类:

#!/usr/bin/env python
from __future__ import print_function
import os, time

class Tail(object):
    def __init__(self, filename):
        self.fd = open(fn, 'r')  # file descriptor
        self.off = self.fd.seek(0, 2)  # end of file: skip the previous contents
        self.buff = list()
        self.last_line = 0
        self.sz = os.fstat(self.fd.fileno()).st_size

    def sleep(self):
        self.off = self.fd.tell()
        while self.sz - self.off == 0:
            self.sz = os.fstat(self.fd.fileno()).st_size
            time.sleep(0.1)

    def follow(self):
        while self.sz - self.off > 0:
            self.buff.append(self.fd.readline())
            self.off = self.fd.tell()
            self.sz = os.fstat(self.fd.fileno()).st_size

    def fetch(self):
        end = self.last_line
        self.last_line = len(self.buff)
        return '\n'.join(self.buff[end:])

...以及使用它的一些示例代码:

if __name__ == '__main__':
    import sys

    if len(sys.argv[1:]) < 1:
        print('Must supply filename', file=sys.stderr)
        sys.exit(1)
    fn = sys.argv[1]

    tail = Tail(fn)
    while True:
        print('... sleeping ...')
        tail.sleep()
        tail.follow()
        print(tail.fetch())

...显示了使用它的一种方式。

我不会使用这门课。我将 Tail.sleep()更改为 Tail.poll(),它会立即返回一个值,指示最后是否有数据准备就绪的文件。我还使用Python Standard Library: select module进行轮询和睡眠。然后,您可以维护一个您一次拖尾的文件列表。不断增长的 Tail.buff 也是一个问题;我会在每次抓取后自动刷新它,或者我添加一个方法来刷新它。