使用子进程调用shell脚本不会运行shell脚本

时间:2016-10-12 10:26:09

标签: linux bash python-2.7 subprocess

我有一个Python脚本循环遍历文件夹,为每个文件创建一个shell命令。

将每个命令写入shell脚本,然后使用subprocess.Popen运行此脚本。 (我需要这样做,因为我还需要在命令工作之前设置环境。)

这是一些伪代码:

def create_shell_script(self):
    '''loop through a folder, create a command for each file and write this to a shell script'''
    # command to run
    base_command="run this"

    #list of files
    command_list=[]

    #loop through the files to create a folder
    for file in my_folder:
        command_list.append(base_command+" "+file)

    # open the shell script
    scriptname="shell.sh"
    shellscript = open(scriptname,'w')
    # set the environment using '.' as using bash below to run shell script
    shellscript.write("#!/bin/bash\n. /etc/profile.d/set_environment\n")

    #loop through commands and write to shellscript
    for command in command_list:
        shellscript.write(command+"\n")

    # use subprocess to run the shell script. Use bash to interpret the environment 
    cmd="bash "+scriptname
    proc = subprocess.Popen([cmd], stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)

当我运行这个python脚本时,只执行shell脚本中的前6个命令。该命令的错误消息表明该命令在被子进程读取时被截断。

当我手动运行shell脚本时,所有命令都按预期执行,所以我知道shell脚本是正确的。

每个命令都是即时的,但我无法想象速度会导致问题。

我确实尝试为每个文件运行一个子进程命令但是我遇到了设置环境的困难,我喜欢创建单个sh脚本的方法,因为它也用作日志文件。

我已经阅读了子流程文档,但没有发现任何内容,谷歌也没有帮助。

2 个答案:

答案 0 :(得分:2)

如果您不了解它的作用,请不要使用Popen创建流程,但在您采取其他步骤之前,它不一定会完成。

您可能只关注subprocess.check_call。此外,将命令存储在文件中是不必要的并且有些脆弱。只需运行subprocess.check_call(['bash', '-c', string_of_commands),其中string_of_commands的命令由换行符或分号分隔。

如果您确实需要或需要使用Popen,则需要在communicate()对象上致电wait()或至少Popen

最后,如果要传递命令列表,请避免使用shell=True; shell的目的是解析一个字符串参数,如果已经完成,或者没有必要,则不执行任何操作(有用)。

以下是重构脚本的尝试。

def create_shell_script(self):
    '''loop through a folder, create a command for each file and write this to a shell script'''

    command_list=['. /etc/profile.d/set_environment']    
    for file in my_folder:
        command_list.append("run this "+file)

    subprocess.check_call(['bash', '-c', ''.join(command_list)],
        stderr=subprocess.PIPE, stdout=subprocess.PIPE)

如果您希望check_call捕获任何单个命令中的任何错误,请传递-e选项,或在命令之前包含set -e,但不要失败(但要注意许多看似无辜的结构在技术上会产生错误,例如falsegrep nomatch file)。

the subprocess module中的大多数函数都是简单的包装器,它们在幕后使用和操作Popen对象。如果其他任何函数都不适合您的场景,则应该只使用基本Popen

如果你真的需要stderrstdout,也许你确实需要Popen,但是你的问题需要描述你的程序如何操纵shell的输入和输出。

答案 1 :(得分:2)

在将命令写入shellscript文件对象之后以及通过Popen运行之前,应该关闭该文件对象。否则,在执行文件之前可能无法完全写入文件。

最优雅的解决方案是使用上下文管理器,它自动关闭文件:

with open(scriptname, "w") as f:
    f.write(...)