【问题标题】:Calling a shell script using subprocess doesn't run all the commands in shell script使用子进程调用 shell 脚本不会运行 shell 脚本中的所有命令
【发布时间】:2017-02-21 02:50:26
【问题描述】:

我有一个循环遍历文件夹的 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 脚本的方法,因为它也可以用作日志文件。

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

【问题讨论】:

  • 您是否尝试在写入文件后和运行前关闭 shellscript 文件对象?否则执行时文件可能不会完全写入。
  • 解决了!谢谢。如果您将其添加为答案,我会将其标记为已接受

标签: linux bash python-2.7 subprocess


【解决方案1】:

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

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

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

最后,如果您要传递命令列表,请避免使用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 的输入和输出。

【讨论】:

    【解决方案2】:

    您应该在向其写入命令之后关闭shellscript 文件对象,然后再通过Popen 运行它。否则,文件在执行之前可能没有完全写入。

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

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

    【讨论】:

      猜你喜欢
      • 2019-11-21
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多