【问题标题】:Thread using subprocess.communicate() throws the error: b'standard in must be a tty\\n'使用 subprocess.communicate() 的线程抛出错误:b'standard in must be a tty\\n'
【发布时间】:2017-08-05 14:41:32
【问题描述】:

我在 Python 的线程中使用 subprocess.Popen() 调用脚本时遇到问题。我已经添加了尽可能多的细节。如果我需要为问题添加更多详细信息,请告诉我。任何帮助是极大的赞赏。提前致谢。

TL:DR subprocess.Popen() 期待标准输入并抛出错误“标准输入必须是 tty”,即使调用的脚本不需要任何标准输入。由于在调用脚本时使用了“su userName -c”,因此开始出现此问题

详情

我正在使用 django 开发一个在后端执行 PERL 脚本的 Web 应用程序。我以 root 用户身份部署应用程序,并打算以触发它的用户身份运行脚本。我将从我们页面中的用户那里获取用户名。

初始命令测试:我以 root 身份登录,并使用以下命令以用户身份启动脚本:

su userName -c ' /path_to_my_script/reImage.pm --host abcdefg --version 1.2.3.5000' 

当我在以 root 用户身份登录的终端中调用它时,上述内容按预期工作并以指定的用户身份运行。所以我知道上面的命令没有问题。

工作流程:

当用户提供输入并在网页中提交时,我会启动一个后台线程来调用上述命令。一旦命令被调用,perl 脚本就会完成它的任务并返回结果。

我在 views.py 中使用以下代码创建线程(注意:我只编写与问题相关的代码行,而不是 views.py 的完整内容)

from threading import Thread

reImageThread = Thread(name = hostName, target = reImageFunction ,args = (hostName,buildNumber,userName) )
reImageThread.daemon = True
reImageThread.start()

reImageFunction 在另一个文件中定义如下:

import subprocess

def reImageFunction(hostName,buildNumber,userName):
    command = " /path_to_my_script/reImage.pm  --host  "+str(hostName)+"  --version "+str(buildNumber)+"  " #Blank space at the end is added intentionally
    command = " su " + userName + " -c \' " + command + " \' " 
    print(command)  #printing for debugging
    #The final command composed would of the format shown in the begenning of this question.
    pipe = subprocess.Popen(command , stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    (result,err) = pipe.communicate()
    print(err) #Printing the err for debugging
    .... #Processing the result
    .... #Processing the result

在 pipe.communicate() 中抛出错误:b'standard in must be a tty\n'

我已经明确提到标准输入将是无。但我不明白为什么子进程仍然需要一些输入。

观察:

  1. 问题出现在我尝试使用 subprocess [pipe.communicate() ] 并运行上述命令的地方。
  2. 当我删除“su username -c”部分时,上面的命令是通过subprocess执行的,没有任何问题,示例如下所示。但这不是预期的功能,所以我必须以触发它的用户身份运行它。

    不带“su userName -c”部分的示例命令:

    /path_to_my_script/reImage.pm --host abcdefg --version 1.2.3.5000

  3. 自从添加了“su userName -c”后,问题开始出现。

  4. 我知道子进程需要一个标准输入,因此即使没有调用命令也会引发错误。
  5. 当我从终端运行脚本时,该脚本以指定用户身份运行,而不需要/提示输入/stdin。一旦调用它就不需要任何标准输入,但我不明白为什么 subprocess.communicate() 期望我提供输入.

我尝试了以下方法:

  1. 我在 pipe.communicate() 没有输入的情况下尝试了相同的功能,但它会引发相同的错误。
  2. 我尝试使用 stdin=subprocess.PIPE 和 pipe.communicate(input='\n')。我试图将新行作为标准输入,但它引发了同样的错误。
  3. 尝试使用 subprocess.call() 即使会引发相同的错误。
  4. 无法使用 os.system(command),因为我需要分析输出。如果 subprocess.Popen() 根本不起作用,将输出写入文件可能是一种解决方法。

感谢您的耐心阅读到最后,

【问题讨论】:

标签: python python-2.7 python-3.x django-views subprocess


【解决方案1】:

我找到了解决问题的方法。

最初,当我尝试从终端使用以下内容时,

sudo -u userName command

由于遇到“权限被拒绝”错误,我无法执行。所以后来我开始从终端使用以下命令

su userName -c "command"

这个 su 命令在终端上运行良好。现在是时候在我的 Web 应用程序中使用它通过 subprocess.invoke(command) 在系统上调用此命令了,这就是我得到“标准输入必须是 tty”错误的地方。

我按照@Ryan 在 cmets 中给出的链接,更改了 root 用户的 /etc/sudoers 文件中的 requiretty,如下所示,这也没有帮助。

Defaults:root !requiretty

最后,当我评论“默认值:requiretty”时(我知道这可能会引发安全问题,但对于这个应用程序来说没问题)并在子进程中使用了 su -userName 命令。调用(),它按预期工作。现在我可以作为请求的用户运行脚本。

感谢阅读,

【讨论】:

    猜你喜欢
    • 2021-11-22
    • 2016-10-10
    • 2014-07-26
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 2023-01-04
    相关资源
    最近更新 更多