python執行shell並獲取結果

TechSynapse發表於2024-07-10

在Python中執行Shell命令並獲取其結果,通常可以使用subprocess模組。這個模組允許我們啟動新的程序,連線到它們的輸入/輸出/錯誤管道,並獲取它們的返回碼。下面是一個詳細的示例,展示瞭如何使用subprocess.run()函式來執行Shell命令並獲取其輸出。

1. 示例一:使用subprocess.run()執行ls命令並獲取結果

這個示例將執行ls命令(在Unix/Linux/macOS系統上列出當前目錄下的檔案和資料夾),並捕獲命令的輸出和返回碼。

import subprocess  
  
# 定義要執行的命令  
command = ['ls', '-l']  # 使用列表形式,更安全,可以避免shell注入攻擊  
  
# 執行命令  
# capture_output=True 參數列示捕獲命令的輸出(stdout和stderr)  
# text=True 參數列示將輸出作為文字處理(Python 3.7+),之前版本使用universal_newlines=True  
result = subprocess.run(command, capture_output=True, text=True)  
  
# 獲取命令的標準輸出  
stdout = result.stdout  
  
# 獲取命令的錯誤輸出(如果有的話)  
stderr = result.stderr  
  
# 獲取命令的返回碼  
returncode = result.returncode  
  
# 列印結果  
print(f"標準輸出:\n{stdout}")  
if stderr:  
    print(f"錯誤輸出:\n{stderr}")  
print(f"返回碼: {returncode}")  
  
# 注意:如果命令成功執行,returncode通常為0;非0值表示有錯誤發生

注意事項

(1)安全性:本例中使用命令列表而非字串來避免shell注入攻擊。當命令和引數以列表形式提供時,Python會直接將它們傳遞給系統,不會透過shell解釋,從而減少了安全風險。

(2)文字與位元組capture_output=Truetext=True(或universal_newlines=True,在舊版本中)的組合使得輸出以文字(字串)形式返回,而不是位元組。這對於處理文字資料很方便,但如果我們需要處理二進位制資料(如影像或影片檔案),則可能需要以位元組形式捕獲輸出。

(3)錯誤處理:透過檢查returncode可以判斷命令是否成功執行。如果returncode不為0,則可能需要根據stderr中的資訊來診斷問題。

(4)跨平臺相容性:本示例中的ls -l命令是Unix/Linux/macOS系統特有的。在Windows系統上,我們可能需要執行不同的命令(如dir),並可能需要調整命令的呼叫方式(例如,使用shell=True,但請注意這會增加安全風險)。

(5)效能考慮:頻繁地啟動外部程序可能會降低程式的效能。如果可能,儘量在Python內部解決問題,或者考慮使用多執行緒/多程序來並行處理外部命令的呼叫。

2. 示例二:使用subprocess.run()函式來執行Shell命令

以下是一個更詳細的程式碼示例,它展示瞭如何在Python中使用subprocess.run()函式來執行Shell命令(在這個例子中是ls -l),並處理可能出現的各種情況,包括成功執行、命令不存在、以及捕獲標準輸出和錯誤輸出。

請注意,這個示例假設我們在一個Unix/Linux/macOS系統上執行,因為ls -l是這些系統的命令。如果我們在Windows上,我們可能需要替換為dir命令,並可能需要調整shell引數的使用(儘管通常建議避免使用shell=True以避免安全風險)。

import subprocess  
  
def run_command(command):  
    """  
    執行給定的命令並返回其輸出和返回碼。  
  
    引數:  
    - command: 要執行的命令,作為列表傳遞(例如 ['ls', '-l']),以避免shell注入。  
  
    返回:  
    - output: 命令的標準輸出(如果有的話)。  
    - error: 命令的錯誤輸出(如果有的話)。  
    - returncode: 命令的返回碼。  
    """  
    try:  
        # 使用subprocess.run()執行命令  
        # capture_output=True表示捕獲stdout和stderr  
        # text=True表示將輸出作為文字處理(Python 3.7+)  
        result = subprocess.run(command, capture_output=True, text=True, check=True)  
        # 如果命令成功執行(沒有異常),則返回其輸出和返回碼  
        return result.stdout, None, result.returncode  
    except subprocess.CalledProcessError as e:  
        # 如果命令執行失敗(返回碼非0),則捕獲CalledProcessError異常  
        # 並返回錯誤輸出、標準輸出(如果有的話)和返回碼  
        return None, e.stderr, e.returncode  
    except Exception as e:  
        # 捕獲其他可能的異常(雖然在這個簡單的例子中可能不太常見)  
        return None, f"An unexpected error occurred: {e}", None  
  
# 定義要執行的命令  
command = ['ls', '-l']  
  
# 執行命令並獲取結果  
output, error, returncode = run_command(command)  
  
# 根據返回的結果列印相應的資訊  
if output:  
    print("標準輸出:")  
    print(output)  
if error:  
    print("錯誤輸出:")  
    print(error)  
if returncode is not None:  
    print(f"返回碼: {returncode}")  
    if returncode == 0:  
        print("命令成功執行。")  
    else:  
        print("命令執行失敗。")

在這個示例中,run_command函式封裝了subprocess.run()的呼叫,並處理了幾種可能的情況:

(1)命令成功執行(返回碼為0):返回標準輸出、None作為錯誤輸出,以及返回碼。

(2)命令執行失敗(返回碼非0):捕獲subprocess.CalledProcessError異常,並返回None作為標準輸出、錯誤輸出,以及返回碼。

(3)其他異常情況:捕獲並返回一條錯誤訊息和None作為返回碼(雖然在這個特定的例子中,由於subprocess.run()通常只丟擲CalledProcessError,所以這部分可能不會被執行)。

請注意,subprocess.run()check=True引數會在命令返回非零退出碼時自動丟擲CalledProcessError異常,這使得我們可以在try-except塊中捕獲它。然而,在這個示例中,我選擇了顯式地捕獲異常,以便能夠更靈活地處理輸出和返回碼。如果我們只想在命令失敗時丟擲異常,並且不關心錯誤處理的具體細節,那麼可以在呼叫subprocess.run()時設定check=True,並讓Python的預設異常處理機制來處理它。

3. shell程式設計及shell命令

3.1 Shell程式設計

Shell程式設計是指使用Shell(也稱為命令列直譯器或命令列介面)作為程式語言來編寫指令碼的過程。Shell是Unix/Linux/macOS等類Unix作業系統中的一個特殊程式,它為使用者提供了一個與作業系統互動的環境。Shell指令碼是一系列Shell命令的集合,這些命令被編寫在文字檔案中,並透過Shell直譯器執行,以實現自動化任務、批處理檔案、管理系統資源等目的。

Shell指令碼具有跨平臺性,因為它們主要依賴於Shell的功能和命令,而這些在大多數類Unix系統中都是相似的。然而,不同的Shell(如Bash、Zsh、Fish等)可能有自己的特性和擴充套件,因此編寫的指令碼可能需要針對特定的Shell進行適配。

Shell程式設計通常包括變數定義、條件判斷、迴圈控制、函式呼叫等程式設計元素,但與傳統程式語言相比,Shell指令碼的語法相對簡單且靈活。

3.2 Shell命令

Shell命令是使用者在Shell環境中輸入的指令,用於執行各種操作,如檔案管理、程式執行、系統管理等。Shell命令可以是Shell內建的,也可以是系統上的外部程式。

(1)內建命令:由Shell本身提供的命令,這些命令在Shell啟動時就已經載入到記憶體中,因此執行速度較快。內建命令不依賴於系統上的其他程式,因此它們在系統啟動時就已經可用。常見的內建命令包括cd(改變目錄)、echo(顯示資訊)、exit(退出Shell)等。

(2)外部命令:也稱為檔案系統命令,這些命令是系統上獨立的程式,通常位於/bin/usr/bin/sbin/usr/sbin等目錄下。當Shell需要執行這些命令時,它會查詢這些目錄來找到對應的程式並執行。常見的外部命令包括ls(列出目錄內容)、cp(複製檔案或目錄)、mv(移動或重新命名檔案或目錄)等。

Shell命令可以透過管道(|)、重定向(><>>)、命令替換(command$(command))等機制進行組合,以實現更復雜的操作。例如,ls -l | grep '^d'命令會列出當前目錄下所有目錄的詳細資訊(ls -l列出詳細資訊,grep '^d'篩選出以d開頭的行,即目錄)。

Shell程式設計和Shell命令是Unix/Linux/macOS等系統使用者日常工作中不可或缺的工具,它們能夠極大地提高使用者的工作效率,並幫助使用者自動化地完成各種任務。

3.3 如何使用Shell程式設計

使用Shell程式設計主要涉及到編寫Shell指令碼,這些指令碼包含了一系列的Shell命令,透過Shell直譯器執行以實現特定的功能。以下是使用Shell程式設計的基本步驟:

3.3.1 選擇Shell

首先,我們需要確定使用哪種Shell。常見的Shell有Bash(Bourne Again SHell,大多數Linux發行版的預設Shell)、Zsh(Z Shell,具有許多增強特性和更好的使用者體驗)、Fish(Friendly Interactive SHell,以使用者友好和易於學習而著稱)等。對於初學者來說,Bash是一個很好的起點,因為它廣泛可用且文件豐富。

3.3.2 編寫Shell指令碼

Shell指令碼通常儲存在以.sh為副檔名的檔案中。我們可以使用任何文字編輯器來編寫Shell指令碼,比如nanovimemacs或簡單的echo和重定向。

以下是一個簡單的Shell指令碼示例,它列印出“Hello, World!”:

#!/bin/bash  
# 這是一個簡單的Shell指令碼示例  
echo "Hello, World!"

在指令碼的第一行,#!/bin/bash被稱為shebang,它告訴系統這個指令碼應該使用哪個直譯器來執行。在這個例子中,它指定了Bash。

3.3.3 儲存指令碼

將我們的指令碼儲存到檔案中,例如hello.sh

3.3.4 賦予執行許可權

在Linux或macOS上,我們需要給指令碼檔案賦予執行許可權,以便能夠直接執行它。我們可以使用chmod命令來做到這一點:

bash複製程式碼

chmod +x hello.sh

這個命令會給hello.sh檔案新增執行許可權。

3.3.5 執行指令碼

現在,我們可以透過以下兩種方式之一來執行我們的指令碼:

直接透過指令碼的路徑和名稱(如果指令碼具有執行許可權):

bash複製程式碼

./hello.sh

注意,我們需要使用./來指定指令碼位於當前目錄下。

使用Shell直譯器來執行指令碼(無論指令碼是否具有執行許可權):

bash複製程式碼

bash hello.sh

這個命令會告訴Bash直譯器來執行hello.sh指令碼中的命令。

相關文章