Python 執行 shell 命令的一些方法

2023-07-17 21:01:15

哈嘍大家好,我是鹹魚

我們知道,python 在自動化領域中被廣泛應用,可以很好地自動化處理一些任務

就比如編寫 Python 指令碼自動化執行重複性的任務,如檔案處理、資料處理、系統管理等需要執行其他程式或者與作業系統互動的任務

那麼今天我們來看一下在 python 中如何執行 shell 命令來與作業系統互動

一般來講,最好是用 python 自帶的函數或模組,而不是直接呼叫其他程式或者作業系統的命令

我們來看一下 python 中有哪些自帶模組或者方法可以實現

pathlib 模組

如果你需要建立或者刪除檔案/目錄,檢查檔案是否存在或者改變許可權等,你完全不需要使用作業系統的命令

你可以完全通過 pathlib 模組來實現,它有你需要的一切,甚至 globos.path 都可以不用

我們來簡單看一下關於這個模組的例子

from pathlib import Path

# 建立一個Path物件表示當前工作目錄
current_directory = Path.cwd()
print("當前工作目錄:", current_directory)

# 建立一個新的目錄
new_directory = current_directory / "my_folder"
new_directory.mkdir()
print("建立新目錄:", new_directory)

# 建立一個新的檔案
new_file = new_directory / "my_file.txt"
new_file.touch()
print("建立新檔案:", new_file)

# 寫入檔案
with new_file.open(mode='w') as f:
    f.write("Hello, World!")

# 讀取檔案內容
with new_file.open() as f:
    content = f.read()
print("檔案內容:", content)

# 遍歷目錄中的檔案
for file in new_directory.iterdir():
    print("檔案:", file)

# 刪除檔案和目錄
new_file.unlink()
new_directory.rmdir()
print("刪除檔案和目錄:", new_file, new_directory)

tempfile 模組

在 Python 中臨時建立和處理檔案時,tempfile 模組提供了方便的方法

它可以在臨時目錄中建立臨時檔案和臨時資料夾,並提供了一些便利的函數和類來管理這些臨時檔案

import tempfile

# 建立臨時檔案
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.write(b'This is a temporary file.')
temp_file.close()

# 列印臨時檔案路徑
print("臨時檔案路徑:", temp_file.name)

# 開啟臨時檔案並讀取內容
with open(temp_file.name, 'r') as f:
    content = f.read()
print("臨時檔案內容:", content)

# 建立臨時目錄
temp_dir = tempfile.TemporaryDirectory()

# 列印臨時目錄路徑
print("臨時目錄路徑:", temp_dir.name)

# 自動清理臨時目錄
temp_dir.cleanup()

shutil 模組

前面我們知道 pathlib 模組滿足了 python 中大多數與檔案相關的需求

如果需要例如複製,移動,刪除或建立檔案,可以使用 shutil 模組

import shutil

# 複製檔案
shutil.copy('source_file.txt', 'destination_folder/')

# 移動檔案
shutil.move('source_file.txt', 'destination_folder/')

# 刪除檔案
shutil.remove('file_to_be_deleted.txt')

# 刪除目錄
shutil.rmtree('directory_to_be_deleted/')

# 建立壓縮檔案
shutil.make_archive('archive', 'zip', 'source_folder/')

# 解壓縮檔案
shutil.unpack_archive('archive.zip', 'destination_folder/')

os 模組

os 模組是 Python 中一個更老的、更底層的模組,提供了與作業系統互動和執行檔案系統操作的功能

但是隨著 python 的發展,越來越多物件導向的、更直觀和易於使用的模組可以供大家使用

對於 os 模組,大家可以瞭解一下就行了

import os

print(os.getenv('PATH'))
# 獲取環境變數PATH的值,並列印
# 範例輸出:/home/martin/.local/bin:/usr/local/sbin:/usr/local/bin:...

print(os.uname())
# 獲取作業系統的資訊,並列印
# 範例輸出:posix.uname_result(sysname='Linux', nodename='...', release='...', version='...', machine='x86_64')

print(os.times())
# 獲取程序的CPU時間資訊,並列印
# 範例輸出:posix.times_result(user=0.01, system=0.0, children_user=0.0, children_system=0.0, elapsed=1740.63)

print(os.cpu_count())
# 獲取可用的CPU核心數量,並列印
# 範例輸出:16

print(os.getloadavg())
# 獲取系統的平均負載,並列印
# 範例輸出:(2.021484375, 2.35595703125, 2.04052734375)

old_umask = os.umask(0o022)
# 設定檔案建立時的許可權掩碼,並將舊的掩碼儲存起來
# 在此處可以執行與檔案相關的操作...

os.umask(old_umask)
# 恢復舊的檔案許可權掩碼

sh 模組

sh 模組不是 python 的標準模組,它是一個第三方模組,在使用之前我們需要安裝它

pip install sh
import sh

# 在 $PATH 中執行任何命令...
print(sh.ls('-la'))
# 執行ls命令並列印輸出
# 範例輸出:
# total 36
# drwxrwxr-x  2 martin martin  4096 apr  8 14:18 .
# drwxrwxr-x 41 martin martin 20480 apr  7 15:23 ..
# -rw-rw-r--  1 martin martin    30 apr  8 14:18 examples.py

ls_cmd = sh.Command('ls')
print(ls_cmd('-la'))  # 顯式呼叫
# 使用Command物件執行ls命令並列印輸出
# 範例輸出與上述相同

# 如果命令不在PATH中:
custom_cmd = sh.Command('/path/to/my/cmd')
custom_cmd('some', 'args') # 執行自定義命令並傳遞引數


with sh.contrib.sudo:
    # 使用'sudo'執行一些操作...
    ...
# 使用'sudo'執行一些操作的上下文環境

當我們通過 sh 模組去執行一些 shell 命令時,sh 模組會嘗試在本地環境變數($PATH)中查詢帶有該名稱的內建 shell 命令或二進位制檔案

如果沒有找到,可以自己新增命令路徑

custom_cmd = sh.Command('/path/to/my/cmd')
custom_cmd('some', 'args')  # 執行自定義命令並傳遞引數

如果要將命令的輸出寫入到檔案裡面,可以使用 _out 引數

#相當於 ip address > /tmp/ipaddr
sh.ip.address(_out='/tmp/ipaddr')

我們在敲 shell 命令時通常會使用到管道符(|),在 sh 模組中通過 _in 引數來實現

print(sh.awk('{print $9}', _in=sh.ls('-la')))
# 等同於 "ls -la | awk '{print $9}'"

print(sh.wc('-l', _in=sh.ls('.', '-1')))
# 等同於  "ls -1 | wc -l"

對於例外處理,我們可以簡單地處理 ErrorReturnCodeTimeoutException 異常

try:
    sh.cat('/tmp/doesnt/exist')
except sh.ErrorReturnCode as e:
    print(f'Command {e.full_cmd} exited with {e.exit_code}')
    # '/usr/bin/cat /tmp/doesnt/exist' 命令結果返回 1

curl = sh.curl('https://httpbin.org/delay/5', _bg=True)
try:
    curl.wait(timeout=3)
except sh.TimeoutException:
    print("Command timed out...")
    curl.kill()