繁体   English   中英

使用python子进程伪造从终端运行cmd

[英]Using python subprocess to fake running a cmd from a terminal

我们有一个供应商提供的python工具(它是字节编译的,我们没有source)。 因此,我们也被锁定使用供应商提供的python 2.4。 实用程序的方法是:

source login.sh
oupload [options]

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

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

现在,当我按他们的方式运行时-工作正常。 它将提示输入用户名和密码,然后执行此操作。

我正在尝试围绕该工具创建包装器,以在该工具运行后执行一些额外的步骤,并为该实用程序提供一些合理的默认值。 我遇到的问题是,在我的一生中,我无法弄清楚如何使用子流程成功完成此操作。 似乎意识到原来的命令并没有直接从终端运行并退出。

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

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

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

因此,似乎正在发送额外的回车符(我尝试rstripping所有选项,无济于事)。

如果不设置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 109, in getAuth
IOError: [Errno 5] Input/output error

Unknown Error Encountered                                              

我尝试了使用p1.communicate,p1.stdin.write()以及shell = False和shell = True的其他变体,但是我没有运气试图找出如何正确发送用户名和密码的方法。 最后的结果是,我尝试查看它们提供的实用程序的字节码-并没有帮助-一旦我使用适当的参数调用了util的主例程,它最终导致了带有线程错误的核心转储。

最后的想法-该实用工具似乎不想“等待”任何输入。 从外壳运行时,它会在“用户名”提示符下暂停。 当运行python的popen时,假设没有输入密码,它会一直冒着火并结束。 我试图查找可能预加载stdin缓冲区的方法-考虑到该进程可能从中读取(如果有的话),但无法确定是否有可能。

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

对于我可以尝试的其他任何想法,将不胜感激。

更新

因此,我通过进一步研究字节码并找出编译命令中缺少的内容来解决了这个问题。

但是,这带来了两个问题-

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

通过将他们的代码包装在try / except子句中,可以轻松解决1。

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

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

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”实际上是命令的返回代码,因此我可能会添加非零情况下的处理。

我试图查找可能预加载标准输入缓冲区的方法

您是否想要创建一个命名的fifo,用用户名/密码信息填充它,然后在读取模式下将其重新打开并将其传递给popen(如popen(..., stdin=myfilledbuffer) )?

您也可以只创建一个普通的临时文件,将数据写入其中,然后以读取模式重新打开它,再次将重新打开的句柄传递为stdin。 (这是我个人不应该做的事情,因为将用户名/密码写入临时文件通常是不好的。OTOH比FIFO更容易测试)

至于潜在原因:我怀疑有问题的软件正在通过非阻塞方法从stdin读取。 不确定连接到终端时为什么可以使用。

AAAANYWAY:根本不需要直接通过Popen使用管道,对吗? 我有点嘲笑这个功能,但我敢打赌它会为您工作:

# you don't actually seem to need popen here IMO -- call() does better for this application.
statuscode = call('echo "%s\n%s\n" | oupload %s' % (username, password, options) , shell=True)

测试status = call('echo "foo\\nbar\\nbar\\nbaz" |wc -l', shell = True) (输出自然为'4'。)

最初的问题是通过仅避免该问题而不使用终端,而是导入由外壳脚本调用的python代码并仅使用它来解决的。

我相信JF Sebastian的答案可能会更适合于最初提出的问题,因此,我建议那些寻求类似问题答案的人不要使用pty模块。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM