python筆記65 - Python3 subprocess執行cmd命令行獲取返回結果


前言

subprocess 模塊允許我們啟動一個新進程,並連接到它們的輸入/輸出/錯誤管道,從而獲取返回值。
Popen 是 subprocess的核心,子進程的創建和管理都靠它處理。

subprocess.Popen

subprocess模塊定義了一個類: Popen

class Popen(object):
    """ Execute a child program in a new process.

    For a complete description of the arguments see the Python documentation.

    Arguments:
      args: A string, or a sequence of program arguments.

      bufsize: supplied as the buffering argument to the open() function when
          creating the stdin/stdout/stderr pipe file objects

      executable: A replacement program to execute.

      stdin, stdout and stderr: These specify the executed programs' standard
          input, standard output and standard error file handles, respectively.

      preexec_fn: (POSIX only) An object to be called in the child process
          just before the child is executed.

      close_fds: Controls closing or inheriting of file descriptors.

      shell: If true, the command will be executed through the shell.

      cwd: Sets the current directory before the child is executed.

      env: Defines the environment variables for the new process.

      universal_newlines: If true, use universal line endings for file
          objects stdin, stdout and stderr.

      startupinfo and creationflags (Windows only)

      restore_signals (POSIX only)

      start_new_session (POSIX only)

      pass_fds (POSIX only)

      encoding and errors: Text mode encoding and error handling to use for
          file objects stdin, stdout and stderr.

    Attributes:
        stdin, stdout, stderr, pid, returncode
    """
    _child_created = False  # Set here since __del__ checks it

    def __init__(self, args, bufsize=-1, executable=None,
                 stdin=None, stdout=None, stderr=None,
                 preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0,
                 restore_signals=True, start_new_session=False,
                 pass_fds=(), *, encoding=None, errors=None):
        """Create new Popen instance."""

常用參數:

  • args:shell命令,可以是字符串或者序列類型(如:str, list,元組)
  • bufsize:緩沖區大小。當創建標准流的管道對象時使用,默認-1。
    0:不使用緩沖區
    1:表示行緩沖,僅當universal_newlines=True時可用,也就是文本模式
    正數:表示緩沖區大小
    負數:表示使用系統默認的緩沖區大小。
  • stdin, stdout, stderr:分別表示程序的標准輸入、輸出、錯誤句柄
  • preexec_fn:只在 Unix 平台下有效,用於指定一個可執行對象(callable object),它將在子進程運行之前被調用
  • shell:如果該參數為 True,將通過操作系統的 shell 執行指定的命令。
  • cwd:用於設置子進程的當前目錄。
  • env:用於指定子進程的環境變量。如果 env = None,子進程的環境變量將從父進程中繼承。
  • encoding:設置編碼類型

使用示例

一個簡單示例,命令行執行pip

import subprocess
p = subprocess.Popen('pip -V',
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     )

# 輸出stdout
print(p.communicate()[0])

得到結果是byte類型的

b'pip 21.1.2 from e:\\python36\\lib\\site-packages\\pip (python 3.6)\r\r\n'

於是可以添加encoding參數utf-8

import subprocess
p = subprocess.Popen('pip -V',
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     encoding='utf-8'
                     )

# 輸出stdout
print(p.communicate()[0])

此時輸出

pip 21.1.2 from e:\python36\lib\site-packages\pip (python 3.6)

如果輸出有中文,會出現解碼異常
輸入java,正常情況是可以輸出中文的

import subprocess
p = subprocess.Popen('java',
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     encoding='utf-8'
                     )

# 輸出stdout
print(p.communicate()[0])

但是運行結果就會解碼異常

Traceback (most recent call last):
  File "D:/tests.py", line 44, in <module>
    print(p.communicate()[0])
  File "E:\python36\lib\subprocess.py", line 830, in communicate
    stdout = self.stdout.read()
  File "E:\python36\lib\codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd3 in position 0: invalid continuation byte

原因是windows系統編碼是gb2312

windows解碼

知道windows系統的編碼后,設置對應的編碼,就可以正常解碼了

import subprocess
p = subprocess.Popen('java',
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     encoding='gb2312'
                     )

# 輸出stdout
print(p.communicate()[0])

得到

用法: java [-options] class [args...]
           (執行類)
   或  java [-options] -jar jarfile [args...]
           (執行 jar 文件)
其中選項包括:
    -d32	  使用 32 位數據模型 (如果可用)
    -d64	  使用 64 位數據模型 (如果可用)
    -server	  選擇 "server" VM
                  默認 VM 是 server.

也可以在拿到輸出結果后decode解碼

import subprocess
p = subprocess.Popen('java',
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     )
# 輸出stdout
result = p.communicate()[0]
print(result.decode('gb2312'))

執行python代碼,得到stdout內容

接下來寫一小段python代碼,看執行結果

# xx.py
print("hello world! 這段包含了中文")

使用subprocess.Popen執行,需設置encoding='utf-8'

import subprocess
p = subprocess.Popen(['python', 'xx.py'],
                     shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     encoding='utf-8'
                     )

# 輸出stdout
print(p.communicate()[0])

運行結果

hello world! 這段包含了中文

如果python代碼有語法異常

# xx.py
print("hello world! 這段包含了中文"x)

此時是可以輸出異常內容的

  File "xx.py", line 1
    print("hello world! 這段包含了中文"x)
                                ^
SyntaxError: invalid syntax

Popen 對象方法

Popen 對象方法用到的幾個方法

  • poll(): 檢查進程是否終止,如果終止返回 returncode,否則返回 None。
  • wait(timeout): 等待子進程終止。
  • communicate(input,timeout): 和子進程交互,發送和讀取數據。
  • send_signal(singnal): 發送信號到子進程 。
  • terminate(): 停止子進程,也就是發送SIGTERM信號到子進程。
  • kill(): 殺死子進程。發送 SIGKILL 信號到子進程。

其它方法參考菜鳥教程https://www.runoob.com/w3cnote/python3-subprocess.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM