【问题标题】:Getting an error in Python when trying to use stdin: io.UnsupportedOperation: fileno尝试使用标准输入时在 Python 中出现错误:io.UnsupportedOperation: fileno
【发布时间】:2015-05-11 15:07:35
【问题描述】:

我正在尝试在 xml 文件中添加标准。我从 subversion 读取了 xml 文件,更新了文件中的一行,现在我正在尝试使用 subporcess.Popen 和 stdin 来创建 jenkins 作业

test = subprocess.Popen('svn cat http://localhost/svn/WernerTest/JenkinsJobTemplates/trunk/smartTemplate.xml --username admin --password admin', stdout=subprocess.PIPE, universal_newlines=True)
job = test.stdout.read().replace("@url@", "http://localhost/svn/WernerTest/TMS/branches/test1")
output = io.StringIO()
output.write(job)
subprocess.Popen('java -jar D:\\applications\\Jenkins\\war\\WEB-INF\\jenkins-cli.jar\\jenkins-cli.jar -s http://localhost:8080/ create-job test7', stdin=output)

我收到以下错误:

Traceback (most recent call last):  File "D:\scripts\jenkinsGetJobs.py", line 20, in <module>
subprocess.Popen('java -jar D:\\applications\\Jenkins\\war\\WEB-INF\\jenkins-cli.jar\\jenkins-cli.jar -s http://localhost:8080/ create-job test7', stdin=output)
File "D:\applications\Python 3.5\lib\subprocess.py", line 914, 
in __init__errread, errwrite) = self._get_handles(stdin, stdout, stderr)
File "D:\applications\Python 3.5\lib\subprocess.py", line 1127, in _get_handles
p2cread = msvcrt.get_osfhandle(stdin.fileno())
io.UnsupportedOperation: fileno

那么如何将更新后的文件传递给下一个子进程呢?

【问题讨论】:

    标签: python python-3.x subprocess


    【解决方案1】:

    使用管道并将数据直接写入该管道:

    test = subprocess.Popen(
        'svn cat http://localhost/svn/WernerTest/JenkinsJobTemplates/trunk/smartTemplate.xml --username admin --password admin',
        stdout=subprocess.PIPE, universal_newlines=True)
    job = test.stdout.read().replace("@url@", "http://localhost/svn/WernerTest/TMS/branches/test1")
    
    jenkins = subprocess.Popen(
        'java -jar D:\\applications\\Jenkins\\war\\WEB-INF\\jenkins-cli.jar\\jenkins-cli.jar -s http://localhost:8080/ create-job test7',
        stdin=subprocess.PIPE, universal_newlines=True)
    jenkins.communicate(job)
    

    Popen.communicate() method 接受第一个参数并将其作为标准输入发送到子进程。

    请注意,我也将 Jenkins 的 universal_newlines argument 设置为 True;另一种方法是让您将 job 字符串显式编码为 Jenkins 将接受的合适编解码器。

    【讨论】:

    • 我现在收到以下错误:TypeError: Can't convert 'bytes' object to str implicitly
    • @Dan:哎呀,我错过了你的universal_newlines 论点;您可能需要在 Jenkins 进程中使用相同的方法来接受 str 作为输入,或者您需要手动显式编码您的数据。
    • 通过使用stdin=subprocess.PIPE,这个调用实际上是从test.stdout.read().replace.....获取正确的数据吗?
    • @Dan:该进程将获取您传入的任何内容作为Popen.communicate() 调用的第一个参数。
    • 对不起,谢谢。我遇到了无法访问 jar 错误,但现在应该是别的了。感谢Martijn的帮助!
    【解决方案2】:

    Popen() 只接受真实文件(至少有效的.fileno())。 @Martijn Pieters♦' answer 展示了如何在内存中一次加载所有数据时传递数据(另外,jenkins 进程直到 svn 产生 all 输出后才会启动)。

    这是一次读取一行的方法(svn 和 jenkins 进程并行运行):

    #!/usr/bine/env python3
    from subprocess import Popen, PIPE
    
    with Popen(svn_cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as svn, \
         Popen(java_cmd, stdin=PIPE, bufsize=1, universal_newlines=True) as java:
        for line in svn.stdout:
            line = line.replace('@url@', 'http://localhost/svn/WernerTest/TMS/branches/test1')
            java.stdin.write(line)
    if java.returncode != 0:
       "handle error"
    

    请参阅下面的 svn_cmdjava_cmd 定义(在 Windows 上您不需要 shlex.split(cmd) -- 注意:不需要 shell=True)。


    如果您不需要替换 @url@,那么您似乎正在尝试模拟:svn_cmd | java_cmd 管道,其中:

    svn_cmd = 'svn cat http://localhost/svn/WernerTest/JenkinsJobTemplates/trunk/smartTemplate.xml --username admin --password admin'
    java_cmd = 'java -jar D:\\applications\\Jenkins\\war\\WEB-INF\\jenkins-cli.jar\\jenkins-cli.jar -s http://localhost:8080/ create-job test7'
    

    最简单的方法是调用shell:

    #!/usr/bin/env python
    import subprocess
    
    subprocess.check_call(svn_cmd + ' | ' + java_cmd, shell=True)
    

    你可以在 Python 中模拟它:

    #!/usr/bin/env python3
    from subprocess import Popen, PIPE
    
    #NOTE: use a list for compatibility with POSIX systems
    with Popen(java_cmd.split(), stdin=PIPE) as java, \
         Popen(svn_cmd.split(), stdout=java.stdin):
        java.stdin.close() # close unused pipe in the parent
        # no more code here (the for-loop is inside an OS code that implements pipes)
    if java.returncode != 0:
       "handle error here"
    

    How do I use subprocess.Popen to connect multiple processes by pipes?

    【讨论】:

    • 如果你不需要替换 @url@ 位是至关重要的。这就是为什么我不只是将帖子复制到您链接的帖子。
    • @MartijnPieters:是的。我一开始就错过了。我已更新答案以包含“替换”步骤。
    猜你喜欢
    • 1970-01-01
    • 2017-10-12
    • 2021-06-25
    • 2011-11-30
    • 2016-10-29
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多