簡體   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