【问题标题】:Use Jython Subprocess to send commands to bash shell使用 Jython Subprocess 向 bash shell 发送命令
【发布时间】:2016-11-04 14:07:13
【问题描述】:


我需要向 Jython 引擎中的 一个 bash shell 发送许多 后续 命令。 使用 os.system(s) 或 subsystem.call(s, ...) 逐个执行命令不起作用,因为每次都会创建新的 shell。

我希望有人有一个想法....以下 3 次测试还不够。

Sample Commands : <br>
cd /home/xxx/dir1/dir2<br>
pwd<br>
cd ..<br>
pwd

在第一个测试中,执行了命令,但只在最后检索输出。

def testRun1():
   # Actual Output
   #     run 0
   #     run 1
   #     run 2
   #     /home/usr/dir1/dir2
   #     /home/usr/dir1
   #     /home/usr
   print 'All output is shown at the end...'
   proc = subprocess.Popen('/bin/bash',
                           shell=True,
                           stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                           )
   for i in range(3):
       print 'run ' + str(i)
       proc.stdin.write('pwd\n')
       proc.stdin.write('cd ..\n')
   output = proc.communicate()[0]
   print output

而“期望的输出”是

 #     run 0
 #     /home/usr/dir1/dir2
 #     run 1
 #     /home/usr/dir1
 #     run 2
 #     /home/usr

第二次试用提供了我们想要的,但输出仅在 jython 脚本中断时显示。

def testRun2():
   # Weird : it is what we want, but all output is blocked until CTRL-C is   pressed
   #     run 0
   #     /home/usr/dir1/dir2
   #     run 1
   #     /home/usr/dir1
   #     run 2
   #     /home/usr
   proc = subprocess.Popen('/bin/bash',
                           shell=True,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           )
   for i in range(3):
       print 'run ' + str(i)
       proc.stdin.write('pwd\n')
       proc.stdin.write('cd ..\n')
       print 'start to print output'
       for line in proc.stdout:
           print(line.decode("utf-8"))
       print_remaining(proc.stdout)
       print 'printed output'

最后一次试用在第二次运行时崩溃,因为流已关闭。

def testRun3():
   # This fails with error
   #  ValueError: I/O operation on closed file
   proc = subprocess.Popen('/bin/bash',
                           shell=True,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           )
   for i in range(3):
       print 'run ' + str(i)
       proc.stdin.write('pwd\n')
       proc.stdin.write('cd ..\n')
       output = proc.communicate()[0]
       print output

【问题讨论】:

  • 1.为什么不编写一个 shell 脚本并运行它呢? 2.你到底在用shell做什么? bash 能做什么而 python/jython 不能?

标签: python bash shell


【解决方案1】:

您遇到的麻烦只是部分与subprocess 有关。对于这项工作,管道从根本上说是错误的 IPC 机制。要将交互式命令解释器置于脚本控制之下,您想要的是pseudoterminal,即便如此,它也不是读写那么简单。

Python 标准库没有任何内置模块可以为您执行伪终端处理,除非它们最近添加了一些我不知道的东西。但是,第三方软件包 pexpect 可以做到,而且它完全适合您想要做的事情。

使用基本的 pexpect API:

import pexpect

def testRun4():
   proc = pexpect.spawn("/bin/bash")
   for i in range(3):
       proc.expect(":^[^$#]*[$#] *")
       print("run", i)
       proc.sendline("pwd")
       proc.expect("^[^$#]*[$#] *")
       print(proc.before)
       proc.sendline("cd ..")

使用pexpect.replwrap,设置起来有点复杂,但循环更整洁:

def testRun5():
    proc = pexpect.replwrap.REPLWrapper(
        "/bin/bash",
        orig_prompt="^[^$#]*[$#] *",
        prompt_change="PS1='{}'; PS2='{}'")

    for i in range(3):
        print("run", i)
        print(proc.run_command("pwd"))
        proc.run_command("cd ..")

【讨论】:

  • 然后我需要查看如何安装 pexpect。目前我正在使用带有 jython.jar 的 Thinclient.sh,并且没有完整的 python (pip) 安装....我将深入研究
  • 恐怕我无法帮助您以“正确”的方式将附加模块安装到 Jython,但 pexpect 没有任何已编译的组件,因此在最坏的情况下您可以只需将其复制到您的应用程序中即可。