本文會涉及到的模組:
- subprocess
- logging
1. subprocess
可以執行shell命令的相關模組和函式有:
os.system
os.spawn
os.popen --廢棄
popen2.* --廢棄
commands.* --廢棄,3.x中被移除
import commands
result = commands.getoutput('cmd')
result = commands.getstatus('cmd')
result = commands.getstatusoutput('cmd')
以上執行shell命令的相關的模組和函式的功能均在 subprocess 模組中實現,並提供了更豐富的功能。
(1) call
執行命令,返回狀態碼(命令正常執行返回0,報錯則返回1)
ret1=subprocess.call("ifconfig")
ret2=subprocess.call("ipconfig")
print(ret1) #0
print(ret2) #1
ret = subprocess.call(["ls", "-l"], shell=False) #shell為False的時候命令必須分開寫
ret = subprocess.call("ls -l", shell=True)
(2) check_call
執行命令,如果執行成功則返回狀態碼0,否則拋異常
subprocess.check_call(["ls", "-l"])
subprocess.check_call("exit 1", shell=True)
(3) check_output
執行命令,如果執行成功則返回執行結果,否則拋異常
subprocess.check_output(["echo", "Hello World!"])
subprocess.check_output("exit 1", shell=True)
(4) subprocess.Popen(...)
用於執行復雜的系統命令
引數 | 註釋 |
---|---|
args | shell命令,可以是字串或者序列型別(如:list,元組) |
bufsize | 指定緩衝。0 無緩衝,1 行緩衝,其他 緩衝區大小,負值 系統緩衝 |
stdin, stdout, stderr | 分別表示程式的標準輸入、輸出、錯誤控制程式碼 |
preexec_fn | 只在Unix平臺下有效,用於指定一個可執行物件(callable object),它將在子程式執行之前被呼叫 |
close_sfs | 在windows平臺下,如果close_fds被設定為True,則新建立的子程式將不會繼承父程式的輸入、輸出、錯誤管道。所以不能將close_fds設定為True同時重定向子程式的標準輸入、輸出與錯誤(stdin, stdout, stderr)。 |
shell | 同上 |
cwd | 用於設定子程式的當前目錄 |
env | 用於指定子程式的環境變數。如果env = None,子程式的環境變數將從父程式中繼承。 |
universal_newlines | 不同系統的換行符不同,True -> 同意使用 \n |
startupinfo | 只在windows下有效,將被傳遞給底層的CreateProcess()函式,用於設定子程式的一些屬性,如:主視窗的外觀,程式的優先順序等等 |
createionflags | 同上 |
import subprocess
ret1 = subprocess.Popen(["mkdir","t1"])
ret2 = subprocess.Popen("mkdir t2", shell=True)
終端輸入的命令分為兩種:
- 輸入即可得到輸出,如:ifconfig
- 輸入進行某環境,依賴再輸入,如:python
import subprocess
obj = subprocess.Popen("mkdir t3", shell=True, cwd='/home/dev',) #在cwd目錄下執行命令
import subprocess
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")
obj.stdin.close()
cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()
print(cmd_out)
print(cmd_error)
import subprocess
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")
out_error_list = obj.communicate()
print(out_error_list)
import subprocess
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
out_error_list = obj.communicate('print("hello")')
print(out_error_list)
2. logging
用於便捷記錄日誌且執行緒安全的模組,不會允許多個人同時操作,不會存在髒資料
(1) 簡單日誌輸出
import logging
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
OUTPUT:
WARNING:root:This is warning message
預設的日誌級別為WARNING,只有【當前寫等級】>=【日誌等級】時,日誌檔案才被記錄。由於info和debug的日誌等級都比warning小,所以上面的程式碼輸出為:“WARNING:root:This is warning message”
日誌級別大小關係如下,當然也可以自己定義日誌級別。
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
(2) 單檔案日誌
import logging
logging.basicConfig(filename='log.log',
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
level=10)
#只有【當前寫等級】大於10時,日誌檔案才被記錄。
logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.critical('critical')
logging.log(10,'log')
logging.basicConfig函式各引數:
函式以及引數 | 註釋 |
---|---|
filename | 指定日誌檔名 |
filemode | 和file函式意義相同,指定日誌檔案的開啟模式,'w'或'a' |
format | 指定輸出的格式和內容,format可以輸出很多有用資訊,如上例所示 |
%(levelno)s | 列印日誌級別的數值 |
%(levelname)s | 列印日誌級別名稱 |
%(pathname)s | 列印當前執行程式的路徑,其實就是sys.argv[0] |
%(filename)s | 列印當前執行程式名 |
%(funcName)s | 列印日誌的當前函式 |
%(lineno)d | 列印日誌的當前行號 |
%(asctime)s | 列印日誌的時間 |
%(thread)d | 列印執行緒ID |
%(threadName)s | 列印執行緒名稱 |
%(process)d | 列印程式ID |
%(message)s | 列印日誌資訊 |
datefmt | 指定時間格式,同time.strftime() |
level | 設定日誌級別,預設為logging.WARNING |
stream | 指定將日誌的輸出流,可以指定輸出到sys.stderr,sys.stdout或者檔案,預設輸出到sys.stderr,當stream和filename同時指定時,stream被忽略 |
(3) 多檔案日誌
對於上述記錄日誌的功能,只能將日誌記錄在單檔案中,如果想要設定多個日誌檔案,logging.basicConfig將無法完成,需要自定義檔案和日誌操作物件。
日誌一:
# 定義檔案
file_1_1 = logging.FileHandler('l1_1.log', 'a', encoding='utf-8')
fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")
file_1_1.setFormatter(fmt)
file_1_2 = logging.FileHandler('l1_2.log', 'a', encoding='utf-8')
fmt = logging.Formatter()
file_1_2.setFormatter(fmt)
# 定義日誌
logger1 = logging.Logger('s1', level=logging.ERROR)
logger1.addHandler(file_1_1)
logger1.addHandler(file_1_2)
# 寫日誌
logger1.critical('1111')
日誌二:
# 定義檔案
file_2_1 = logging.FileHandler('l2_1.log', 'a')
fmt = logging.Formatter()
file_2_1.setFormatter(fmt)
# 定義日誌
logger2 = logging.Logger('s2', level=logging.INFO)
logger2.addHandler(file_2_1)
如上述建立的兩個日誌物件
當使用【logger1】寫日誌時,會將相應的內容寫入 l1_1.log 和 l1_2.log 檔案中
當使用【logger2】寫日誌時,會將相應的內容寫入 l2_1.log 檔案中