【问题标题】:Pass integer input argument for Popen.communicate()为 Popen.communicate() 传递整数输入参数
【发布时间】:2016-05-06 18:29:02
【问题描述】:

我对 python 很陌生,现在我被一些东西困住了。我正在使用python 2.7。 我正在尝试在我编写了 python 脚本的 shell 中自动化一个命令。我必须向使用 Popen.communicate(input='2') 的 shell 输入一个整数值。虽然输入是传递的,但它是作为字符串传递的,子进程需要它作为数值。当我尝试将它作为 Popen.communicate(input=2) 之类的数字值传递时,它会引发以下错误:

TypeError: 'int' 对象没有属性 '__getitem__'

那么有没有办法将输入作为数值发送?

这是我使用的代码:

import sys
from subprocess import Popen, PIPE, STDOUT

cmd = ["sudo", "./sbt", "project java-examples", "run"]
proc = Popen(cmd, bufsize=4096, shell=False, stdout=PIPE, stdin=PIPE, stderr=STDOUT)

string = proc.communicate(input='2')
print string[0]
proc.stdin.close()

编辑: 抱歉,我一开始没有提到这一点。 我的 java 应用程序有多个主类,因此编译器会询问要执行哪个类,并且我每次都需要输入相同的数字,这就是我试图自动化它的原因。当我传递一个字符串值时,它会抛出 numberformatexception。

【问题讨论】:

  • docs.python.org/2/library/… - “可选输入参数应该是要发送到子进程的字符串”,因此不,这是不可能的。这很好,因为管道是文本的,而不是 Python 对象,所以它需要一个“数字”值 2 而不是字符串“2”是没有意义的。那是什么意思?当你通过input='2' 时实际发生了什么?
  • @TessellatingHeckler 子进程需要一个数字输入,而不是字符串输入,所以当我传递一个字符串值时,它会抛出一个“无效数字:java.lang.NumberFormatException:对于输入字符串:”“”。并且更改子进程的输入类型不是一种选择,因为它是由 shell 本身而不是子进程提示的。

标签: python-2.7 shell popen


【解决方案1】:

最后我找到了解决方案。 这个问题的解决方法很简单


当我改变时

string = proc.communicate(input='2')

string = proc.communicate(input='2\n')

效果很好。

【讨论】:

  • 不接受 EOF 作为行尾的代码已损坏,即如果子进程接受 '2\n' 作为整个输入,那么它也应该接受 '2'
【解决方案2】:

您正在调用的./sbt 进程需要能够读取字符串并将其转换为整数。无法将原生 Python 类型发送到只有 Popen 的子进程。

另一个潜在的解决方案是将数据序列化为 JSON、YAML、XML、python pickle 格式等 - 选择您的标准。但是,当您传递一个整数时,这似乎太过分了。

【讨论】:

  • 更改子进程的输入类型不是一个选项,因为它是由 shell 本身而不是子进程提示的。
  • 我想你还是不明白我们在说什么。 stdin/stdout/stderr 仅是 str (Python3: bytes)。这就是 Unix 的工作原理。子进程可以将数字读取为字符串,然后将其转换为整数 - 这是最有可能的。如果您引用生成过程的代码,我们可能会为您提供更多帮助。
  • 我正在尝试运行一个 java 应用程序,它有多个主类。所以提示“检测到多个主类,选择一个运行:[1] TestProducer [2] kafka.examples.SimpleConsumerDemo [3] kafka.examples.KafkaConsumerProducerDemo”
  • 所以听起来你的 java 应用程序正在读取一个字符串。它可能会验证字符串以确保它只能是 0 到 9 的数字,但它仍然是一个字符串。所以你上面引用的代码proc.communicate(input='2') 应该是正确的。我认为可以安全地假设它不起作用。我建议你开始一个新问题,提供更多关于作为字符串传递的案例的详细信息,然后询问它为什么不起作用。
【解决方案3】:

管道是 Unix 中的字节流。没有数字输入——只有“字符串”输入(没有数字或其他任何东西——只有字节)。

如果您的子 java 进程需要一个数字作为其 ascii 表示,那么如果子进程从标准输入 (System.in) 读取,则 .communicate(input='2') 已经正确。如果 java 进程直接从控制台读取 (System.console().readLine()),那么 stdin=PIPE 将不起作用(在这种情况下,您可能需要提供一个 pty,例如,使用 pexpect)。

例如,从标准输入读取一个整数并用 Java 打印回来:

import java.util.Scanner;

public class ReadInteger
{
  public static void main(String args[])
  {
    Scanner in = new Scanner(System.in);
    int i = in.nextInt();
    System.out.println("got " + i);
  }
}

通过管道将整数从 Python 进程传递给 Java 进程:

#!/usr/bin/env python2
from subprocess import Popen, PIPE

p = Popen(['java', '-ea', 'ReadInteger'], stdin=PIPE, stdout=PIPE)
output = p.communicate(input='2')[0]
print 'from java {' + output.strip() + '}'

有效(输出为from java {got 2})。

如果子进程改为从控制台读取,它将停止工作:

public class ReadIntegerConsole
{
  public static void main(String args[])
  {
    int i = Integer.parseInt(System.console().readLine());
    System.out.println("got " + i);
  }
}

相同的 python 脚本导致NullPointerException(输出为from java {}),因为System.console() 在此处为空(stdin 被重定向)。如果我们提供一个伪 tty,它就可以工作:

#!/usr/bin/env python2
import pexpect # $ pip install pexpect

child = pexpect.spawn('java -ea ReadIntegerConsole')
child.setecho(False)
child.sendline('2')
child.expect(pexpect.EOF)
child.close()
print "from java {" + child.before.strip() + "}"

有效(输出为from java {got 2})。

【讨论】:

  • 感谢您的详细回答,但恐怕它并不能解决我的问题。我的确切问题是 Java 编译器找到了多个主类并询问要执行哪个主类,我每次都必须输入相同的选项,这就是我试图自动化它的原因。当传递一个字符串 '2' 时,它会抛出一个 numberformatexception。
  • @aladeen:不要在 cmets 中为您的问题信息添加重要信息,而是编辑您的问题(包括完整的堆栈跟踪)。 pexpect 应该可以工作,即使你用来启动你的 java 程序的工具直接与控制台一起工作。这是一个复杂的解决方案;我会尝试传递一个特定的 Java 类名,例如 ['java', '-ea', 'ReadInteger']。你应该从我的回答中明白两件事:(1)“没有数字或其他任何东西——只有字节”(2)A process may bypass stdin and read the input from the console directly.
猜你喜欢
  • 2015-12-09
  • 2020-02-17
  • 1970-01-01
  • 2020-12-23
  • 2014-01-22
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 2016-04-09
相关资源
最近更新 更多