使用 python 子进程伪造从终端运行 cmd [英] Using python subprocess to fake running a cmd from a terminal

查看:35
本文介绍了使用 python 子进程伪造从终端运行 cmd的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个供应商提供的 python 工具(这是字节编译的,我们没有源代码).因此,我们也被锁定使用供应商提供的 python 2.4.util的方式是:

We have a vendor-supplied python tool ( that's byte-compiled, we don't have the source ). Because of this, we're also locked into using the vendor supplied python 2.4. The way to the util is:

source login.sh
oupload [options]

login.sh 只设置了几个 env 变量,然后是 2 个别名:

The login.sh just sets a few env variables, and then 2 aliases:

odownload () {
${PYTHON_CMD} ${OCLIPATH}/ocli/commands/word_download_command.pyc "$@"
}
oupload () {
${PYTHON_CMD} ${OCLIPATH}/ocli/commands/word_upload_command.pyc "$@"
}

现在,当我按照他们的方式运行时 - 工作正常.它会提示输入用户名和密码,然后做它的事情.

Now, when I run it their way - works fine. It will prompt for a username and password, then do it's thing.

我正在尝试围绕该工具创建一个包装器,以便在它运行后执行一些额外的步骤,并为该实用程序提供一些合理的默认值.我遇到的问题是,在我的一生中,我无法弄清楚如何使用子流程来成功地做到这一点.它似乎意识到原始命令不是直接从终端和保释金运行的.

I'm trying to create a wrapper around the tool to do some extra steps after it's run and provide some sane defaults for the utility. The problem I'm running into is I cannot, for the life of me, figure out how to use subprocess to successfully do this. It seems to realize that the original command isn't running directly from the terminal and bails.

我创建了一个 '/usr/local/bin/oupload' 并从原始 login.sh 复制.唯一的区别是我最后没有做别名,而是实际运行命令.

I created a '/usr/local/bin/oupload' and copied from the original login.sh. Only difference is instead of doing an alias at the end, I actually run the command.

然后,在我的 python 脚本中,我尝试运行我的新 shell 脚本:

Then, in my python script, I try to run my new shell script:

if os.path.exists(options.zipfile):
    try:
        cmd = string.join(cmdargs,' ')
        p1 = Popen(cmd, shell=True, stdin=PIPE)

但我明白了:

Enter Opsware Username: Traceback (most recent call last):
File "./command.py", line 31, in main
File "./controller.py", line 51, in handle
File "./controllers/word_upload_controller.py", line 81, in _handle
File "./controller.py", line 66, in _determineNew
File "./lib/util.py", line 83, in determineNew
File "./lib/util.py", line 112, in getAuth
Empty Username not legal

Unknown Error Encountered                                              

SUMMARY:
Name: Empty Username not legal
Description: None

所以似乎发送了一个额外的回车(我尝试删除所有选项,但没有帮助).

So it seemed like an extra carriage return was getting sent ( I tried rstripping all the options, didn't help ).

如果我不设置 stdin=PIPE,我会得到:

If I don't set stdin=PIPE, I get:

Enter Opsware Username: Traceback (most recent call last):
File "./command.py", line 31, in main
File "./controller.py", line 51, in handle
File "./controllers/word_upload_controller.py", line 81, in _handle
File "./controller.py", line 66, in _determineNew
File "./lib/util.py", line 83, in determineNew
File "./lib/util.py", line 109, in getAuth
IOError: [Errno 5] Input/output error

Unknown Error Encountered                                              

我尝试过使用 p1.communicate、p1.stdin.write() 以及 shell=False 和 shell=True 的其他变体,但我没有尝试弄清楚如何正确发送用户名和密码.作为最后一个结果,我尝试查看他们提供的实用程序的字节码 - 它没有帮助 - 一旦我使用正确的参数调用了实用程序的主例程,它最终会出现内核转储和线程错误.

I've tried other variations of using p1.communicate, p1.stdin.write() along with shell=False and shell=True, but I've had no luck in trying to figure out how to properly send along the username and password. As a last result, I tried looking at the byte code for the utility they provided - it didn't help - once I called the util's main routine with the proper arguments, it ended up core dumping w/ thread errors.

最后的想法 - 该实用程序似乎不想等待"任何输入.从 shell 运行时,它会在用户名"提示处暂停.当通过 python 的 popen 运行时,它只是通过并结束,假设没有给出密码.我试图查找可能预加载 stdin 缓冲区的方法 - 认为如果可用,进程可能会从中读取,但无法确定是否可能.

Final thoughts - the utility doesn't want to seem to 'wait' for any input. When run from the shell, it pauses at the 'Username' prompt. When run through python's popen, it just blazes thru and ends, assuming no password was given. I tried to lookup ways of maybe preloading the stdin buffer - thinking maybe the process would read from that if it was available, but couldn't figure out if that was possible.

我试图避免使用 pexpect,主要是因为我们必须使用供应商提供的 python 2.4,因为他们提供了预编译库,并且我试图将脚本的分发保持在尽可能小的占用空间- 如果我必须,我必须,但我宁愿不使用它(老实说,我也不知道它在这种情况下是否有效).

I'm trying to stay away from using pexpect, mainly because we have to use the vendor's provided python 2.4 because of the precompiled libraries they provide and I'm trying to keep distribution of the script to as minimal a footprint as possible - if I have to, I have to, but I'd rather not use it ( and I honestly have no idea if it works in this situation either ).

如果您对我还可以尝试的其他方法有任何想法,我们将不胜感激.

Any thoughts on what else I could try would be most appreciated.

更新

所以我通过深入研究字节码并找出编译命令中遗漏了什么来解决这个问题.

So I solved this by diving further into the bytecode and figuring out what I was missing from the compiled command.

然而,这带来了两个问题 -

However, this presented two problems -

  1. 供应商代码在调用时正在执行退出
  2. 供应商代码正在写入标准输出,我需要对其进行存储和操作(它包含上传的 pkg 的 ID).我不能只是重定向标准输出,因为供应商代码仍在要求输入用户名/密码.

1 通过将他们的代码包装在 try/except 子句中很容易解决.

1 was solved easy enough by wrapping their code in a try/except clause.

2 是通过执行类似以下操作解决的:https://stackoverflow.com/a/616672/677373

2 was solved by doing something similar to: https://stackoverflow.com/a/616672/677373

我使用了 cStringIO,而不是日志文件.我还必须实现一个虚假的flush"方法,因为供应商代码似乎正在调用该方法并抱怨我为 stdout 提供的新 obj 没有提供它 - 代码最终看起来像:

Instead of a log file, I used cStringIO. I also had to implement a fake 'flush' method, since it seems the vendor code was calling that and complaining that the new obj I had provided for stdout didn't supply it - code ends up looking like:

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = StringIO()

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)

    def flush(self):
        self.terminal.flush()
        self.log.flush()

if os.path.exists(options.zipfile):
    try:
        os.environ['OCLI_CODESET'] = 'ISO-8859-1'
        backup = sys.stdout
        sys.stdout = output = Logger()
        # UploadCommand was the command found in the bytecode
        upload = UploadCommand()
        try:
            upload.main(cmdargs)
        except Exception, rc:
            pass
        sys.stdout = backup
        # now do some fancy stuff with output from output.log

我应该注意,我只是在 except: 子句中执行pass"的唯一原因是,except 子句总是被调用.'rc' 实际上是命令的返回码,所以我可能会添加对非零情况的处理.

I should note that the only reason I simply do a 'pass' in the except: clause is that the except clause is always called. The 'rc' is actually the return code from the command, so I will probably add handling for non-zero cases.

推荐答案

通过避免该问题而不使用终端,而是导入 shell 脚本调用的 python 代码并仅使用它,解决了原始问题.

The original question was solved by just avoiding the issue and not using the terminal and instead importing the python code that was being called by the shell script and just using that.

我相信 J.F. Sebastian 的答案可能更适合最初提出的问题,所以我建议人们寻找类似问题的答案,看看使用 pty 模块的路径.

I believe J.F. Sebastian's answer would probably work better for what was originally asked, however, so I'd suggest people looking for an answer to a similar question look down the path of using the pty module.

这篇关于使用 python 子进程伪造从终端运行 cmd的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆