python的學習筆記-打造python命令列工具(第二天)

czxin788發表於2017-12-30
python的學習筆記

第二天(2017-12-30)打造python命令列工具
宣告:本人是每天學習賴明星《python linux系統管理與自動化運維》一書後,整理成自己的筆記,如有需要,請購買作者正版書,謝謝

1、使用sys.argv獲取命令列引數
sys.argv是一個儲存命令列引數的普通列表。

1
[root@localhost ~]# cat test_argv.py 
2
#!/bin/python
3
from __future__ import print_function
4
import sys
5
6
print (sys.argv)

[root@localhost ~]# python test_argv.py localhost 3306
['test_argv.py', 'localhost', '3306']

1
[root@localhost ~]# cat test_argv1.py
2
#!/bin/python
3
from __future__ import print_function
4
import os
5
import sys
6
7
def main():
8
    sys.argv.append("")
9
    filename = sys.argv[1]
10
    if not os.path.isfile(filename):
11
        raise SystemExit(filename + ' does not exists')
12
    elif not  os.access(filename,os.R_OK):
13
        raise SystemExit(filename + ' is not accessible')
14
    else:
15
        print(filename + ' is accessible')
16
17
if __name__ == '__main__':
18
    main()



[root@localhost ~]# python test_argv1.py  2.txt
2.txt is accessible

2、使用sys.stdin和fileinput讀取標準輸入
在python標準庫的sys庫中,有三個檔案描述符,分別是stdin,stdout,stderr,分別是標準輸入、標準輸出和標準出錯。
a)、從標準輸入讀取內容(sys.stdin)
1
[root@localhost ~]# cat read.stdin.py 
2
#!/usr/bin/python
3
from __future__ import print_function
4
import sys
5
6
7
for line in sys.stdin:
8
    print(line,end="")

接下來我們就可以像shell一樣,透過標準輸入給程式輸入內容:
[root@localhost ~]# cat /etc/passwd |python read.stdin.py 
[root@localhost ~]# python read.stdin.py < /etc/passwd

b)、呼叫檔案物件讀取內容(sys.stdin)
1
[root@localhost ~]# cat read.stdin.py 
2
#!/usr/bin/python
3
#coding:utf-8
4
from __future__ import print_function
5
import sys
6
7
def get_content():
8
    return sys.stdin.readlines() #呼叫readlines函式將標準輸入的內容讀取到一個列表中
9
10
11
print(get_content())
12

[root@localhost ~]# cat /etc/passwd |python read.stdin.py

我們完全可以在python中用stdin代替awk進行資料處理,

fileinput可以對多檔案進行處理,也可以代替awk。使用fileinput可以依次讀取命令列中給出的多個檔案,也就是說fileinput會遍歷sys.argv[1:]列表,並按行依次讀取列表中的問題,如果該檔案為空,則fileinput預設讀取標準輸入中的內容。

在大多數情況下,我們直接呼叫fileinput模組中的Input方法按行讀取內容即可。

例子:
1
[root@localhost ~]# cat read.stdin.py 
2
#!/usr/bin/python
3
#coding:utf-8
4
from __future__ import print_function
5
import fileinput
6
7
8
for line in fileinput.input():
9
    print(line,end="")
10
11
fileinput比sys.stdin更加靈活。fileinput既可以從標準輸入中讀取資料,也可以從檔案中讀取資料,如下所示:
[root@localhost ~]# cat /etc/passwd |python read.stdin.py 
[root@localhost ~]# python read.stdin.py < /etc/passwd
[root@localhost ~]# python read.stdin.py /etc/passwd
[root@localhost ~]# python read.stdin.py /etc/passwd /etc/hosts

因為fileinput可以讀取多個檔案內容,所以,fileinput提供了一些方法讓我們知道當前讀取的內容屬於哪個檔案,fileinput中常見的方法有:
filename:當前正在讀取的檔名;
fileno:檔案的描述符
filelineno:正在讀取的行是當前檔案的第幾行;
isfirstline:正在讀取的行是否當前檔案的第一行;
isstdin fileinput:正在讀取的檔案還是直接從標準輸入讀取的內容。

例子:
1
[root@localhost ~]# cat test.py 
2
#!/usr/bin/python
3
#coding:utf-8
4
from __future__ import print_function
5
import fileinput
6
7
for line in fileinput.input():
8
    meta = [fileinput.filename(),fileinput.fileno(),fileinput.filelineno(),fileinput.isfirstline(),fileinput.isstdin()]
9
    print(*meta, end=" ") #注意星號作用
10
    print(line,end=" ")
11

[root@localhost ~]# python test.py  /etc/passwd /etc/hosts

說明:
星號作用於函式中的形式引數,使函式具有接收可變引數的功能
星號作用於函式呼叫時的實際引數,單星號對應元組,雙星號對應字典。對於元組,將元組中對應值傳給對應引數,對於字典,將字典中對應key-value對指定的值傳給對應的引數。

3、使用SystemExit異常列印錯誤資訊

與標準輸入sys.stdin類似,我們可以直接使用sys.stdout和sys.stderr向標準輸出和錯誤輸出中輸出內容。
1
root@localhost ~]# cat test_stdout_stderr.py 
2
#/usr/bin/python
3
#coding-utf8
4
import sys
5
6
sys.stdout.write('hello')
7
sys.stderr.write('world')
8
9
預設情況下,hello和world都會輸出到命令列。我們可以透過重定向來驗證hello被輸出到標準輸出,world被輸出到了錯誤輸出,如下:
[root@localhost ~]# python test_stdout_stderr.py 
worldhello

[root@localhost ~]# python test_stdout_stderr.py  > /dev/null 
world

[root@localhost ~]# python test_stdout_stderr.py  2> /dev/null 
hello


如果我的python程式執行失敗,需要在標準錯誤中輸出錯誤資訊,然後以非零的返回碼退出程式,那麼我們就需要使用sys.stderr,如下:
1
[root@localhost ~]# cat test_stdout_stderr.py 
2
#/usr/bin/python
3
#coding-utf8
4
import sys
5
6
sys.stderr.write('error message')
7
sys.exit(1)
8

[root@localhost ~]# python test_stdout_stderr.py 
error message[root@localhost ~]# echo $?
1
4、使用getpass庫讀取密碼
getpass是一個非常簡單的標準庫,主要包含getuser函式和getpass函式。前者用來從環境變數中獲取使用者名稱,後者用來等待使用者輸入密碼。

1
[root@localhost ~]# cat getpass.py
2
#/usr/bin/python
3
from __future__ import print_function
4
import getpass
5
6
user = getpass.getuser()
7
passwd = getpass.getpass('please your password:')
8
print(user,passwd)

5、使用configparse解析配置檔案
mysql資料庫客戶端預設使用/etc/my.cnf中的配置。
配置檔案的好處是,配置成功後不需要每次使用時都指定相應的引數。
一個典型的配置檔案包含一到多個章節(section),每個章節下可以包含一到多個選項‘(option),如MySQL的配置配置檔案:
[client]
port         = 3306
user        = mysql
password = mysql
host         = 127.0.0.1

[mysqld]
basedir  = /usr
datadir  =  /var/lib/mysql
tmpdir  =  /tmp
skip-external-locking

上面包含了client和mysqld兩個章節,每個章節下有不同的選項。

1
In [3]: import ConfigParser
2
3
In [4]: cf = ConfigParser.ConfigParser(allow_no_value=True)
4
說明:allow_no_values預設為False,表示配置檔案中不允許沒有值。如上面的skip-external-locking這個選項就沒有值。

有了ConfigParser物件以後,就可以使用read方法從配置檔案中讀取配置內容。
1
In [4]: cf.read('my.cnf')
2
Out[4]: ['my.cnf']
3

ConfigParser中有很多方法,其中與讀取配置檔案,判讀檔案檔案相關的引數有:
1)sections:返回一個包含所有章節的列表;
2)has_section:判斷章節是否存在
3)items:以元組的形式返回所有選項;
4)options:返回一個章節下所有選項的列表;
5)has_option:判斷每個選項是否存在;
6)get、getboolean、getinit、getfloat:獲取選項的值。get預設是返回的字串,要返回數值使用getint,返回布林型,使用getboolean

1
In [6]: cf.sections()
2
Out[6]: ['client', 'mysqld']
3
4
In [10]: cf.has_section('mysqld')
5
Out[10]: True
6
7
In [14]: cf.has_option('client','port')
8
Out[14]: True
9
10
11
In [18]: cf.options('client')
12
Out[18]: ['port', 'user', 'password', 'host']
13
14
15
In [20]: cf.get('client','host')
16
Out[20]: '127.0.0.1'
17
18
19
In [22]: cf.getint('client','port')
20
Out[22]: 3306
21
22
In [23]: cf.get('client','port')
23
Out[23]: '3306'
24

ConfigParser也提供了血多方法便於我們修改配置檔案,如下:
1)remove_section:刪除一個章節;
2)add_section:新增一個章節;
3)remove_option:刪除一個選項;
4)set:新增一個選項;
5)write:將ConfigParser物件中的物件儲存到檔案中。

1
In [24]: cf.remove_section('client')
2
Out[24]: True
3
In [25]: cf.add_section('mysql')
4
In [26]: cf.set('mysql','host','127.0.0.1')
5
In [28]: cf.set('mysql','port','3306')
6
In [32]: cf.write(open('my_copy.cnf','w'))
7
修改完後,新的my_copy.cnf檔案內容如下:
[root@localhost ~]# cat my_copy.cnf 
[mysqld]
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
skip-external-locking

[mysql]
host = 127.0.0.1
port = 3306


6、使用argparse解析命令列引數
6.1 ArgumentParse解析器
使用argparse解析命令列引數時,首先需要建立一個解析器,如下:
1
In [33]: import argparse
2
In [34]: parser = argparse.ArgumentParser()
3
ArgumentParser物件的add_argument方法有如下引數:
1)name/flags:引數的名字;
2)action:遇到引數時的動作,預設值是store;
3)nargs:引數的個數,可以是具體的數字,或者是+號與*號,其中+號表示0或者多個引數,+號表示1或者多個引數;
4)const action和nargs:需要的常量值
5)default:不指定引數時的預設值
6)type:引數的型別;
7)choices:引數允許的值;
8)required:可選引數是否可以省略;
9)help:引數的幫助資訊;
10)metavar:在usage說明中的引數名稱;
11)dest:解析後引數名稱

解析引數需要用ArgumentParser物件的parse_args方法,該方法返回一個namespace物件,獲取物件後,引數值透過屬性的方式進行訪問。

1
[root@localhost ~]# cat test2.py 
2
#/usr/bin/python
3
#cording:utf-8
4
from __future__ import print_function
5
#匯入argparser庫
6
import argparse
7
8
9
def __argparse():
10
    #建立一個ArgumentParser解析器
11
    parser = argparse.ArgumentParser(description="This is description")
12
    #透過add_argument函式新增選項
13
    parser.add_argument('--host',action='store',dest='server',default="localhost",help='connect to server')
14
    parser.add_argument('-t',action='store_true',default=False,dest='boolean_switch',help='set a switch to true')
15
    #呼叫parse_args函式解析命令列引數
16
    return parser.parse_args()
17
    
18
19
def main():
20
    parser = __argparse()
21
    print(parser)
22
    #透過parser.server獲取--host選項的值
23
    print('host = ',parser.server)
24
    #透過parser.boolean_switch獲取-t選項的值
25
    print('boolean_switch=',parser.boolean_switch)
26
27
if __name__ == '__main__':
28
    main()
29

在上面的例子中,我們首先匯入argparser庫,然後在__argparse函式中建立一個ArgumentParser解析器,並透過add_argument函式新增選項。選項新增完後,呼叫parse_args函式解析命令列引數。在main函式中,我們可以透過parser.server獲取--host選項的值,透過parser.boolean_switch獲取-t選項的值。
1
[root@localhost ~]# python test2.py 
2
Namespace(boolean_switch=False, server='localhost')
3
host = localhost
4
boolean_switch= False
5
6
7
[root@localhost ~]# python test2.py  --host=127.0.0.1 -t
8
Namespace(boolean_switch=True, server='127.0.0.1')
9
host =  127.0.0.1
10
boolean_switch= True
11
使用argparser進行引數解析還有一個好處是,它能根據我們的選項定義自動生成幫助資訊,例如:
1
[root@localhost ~]# python test2.py  --help
2
usage: test2.py [-h] [--host SERVER] [-t]
3
4
This is description
5
6
optional arguments:
7
  -h, --help     show this help message and exit
8
  --host SERVER  connect to server
9
  -t             set a switch to true
10
6.2 模仿MySQL客戶端的命令列引數
1
[root@localhost ~]# cat mysql.py 
2
#/usr/bin/python
3
#coding:utf-8
4
from __future__ import print_function
5
import argparse
6
7
8
def __argparse():
9
    parser = argparse.ArgumentParser(description='A python-mysql client')
10
    parser.add_argument('--host',action='store',dest='host',required=True,help='connect to host')
11
    parser.add_argument('-u','--user',action='store',dest='user',required=True,help='user for login')
12
    parser.add_argument('-p','--password',action='store',dest='password',required=True,help='password to use where connect to server')
13
    parser.add_argument('-P','--port',action='store',dest='port',default=3306,type=int,help='port number to use for connection or 3306 for default')
14
    parser.add_argument('-v','--version',action='version',version='%(prog)s 0.1')
15
    return parser.parse_args()
16
17
18
def main():
19
    parser = __argparse()
20
    conn_args = dict(host=parser.host,user=parser.user,password=parser.password,port=parser.port)
21
    print(conn_args)
22
23
if __name__ == '__main__':
24
    main()
25


1
[root@localhost ~]# python mysql.py --help
2
usage: mysql.py [-h] --host HOST -u USER -p PASSWORD [-P PORT] [-v]
3
4
A python-mysql client
5
6
optional arguments:
7
  -h, --help            show this help message and exit
8
  --host HOST           connect to host
9
  -u USER, --user USER  user for login
10
  -p PASSWORD, --password PASSWORD
11
                        password to use where connect to server
12
  -P PORT, --port PORT  port number to use for connection or 3306 for default
13
  -v, --version         show program's version number and exit
14

1
[root@localhost ~]# python mysql.py --host=127.0.0.1 -u=root -p=123
2
{'host': '127.0.0.1', 'password': '123', 'port': 3306, 'user': 'root'}
3

7、使用logging記錄日誌
7.1、python的logging模組
注意:1.命名py指令碼時,不要與python預留字,模組名等相同,否則執行的指令碼會報錯
1
[root@localhost ~]# cat test_logging.py 
2
#!/usr/local/bin/python
3
#coding:utf-8
4
import logging
5
6
7
logging.debug('debug message')
8
logging.info('info message')
9
logging.warn('warn message')
10
logging.error('error message')
11
logging.critical('critical message')
12
程式的執行結果如下:
[root@localhost ~]# python test_logging.py 
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

7.2、配置日誌格式
在使用logging記錄日期之前,我們需要進行一些簡單的設定,如下:
1
[root@localhost ~]# cat test_logging.py 
2
#!/usr/local/bin/python
3
#coding:utf-8
4
import logging
5
6
logging.basicConfig(filename='app.log',level=logging.INFO)
7
8
logging.debug('debug message')
9
logging.info('info message')
10
logging.warn('warn message')
11
logging.error('error message')
12
logging.critical('critical message')
13

[root@localhost ~]# python test_logging.py 
[root@localhost ~]# cat app.log 
INFO:root:info message
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

我們先了解logging模組中的幾個概念,即Logger、Handler及Formatter。
Logger:日誌記錄器,是應用程式中能直接使用的介面;
Handler:日誌處理器,用以表明將日誌儲存到什麼地方以及儲存多久;
Formatter:格式化,用以格式化日誌的輸出格式。

在典型的使用場景中,一個日誌記錄器使用一個日誌處理器,一個日誌處理器使用一個日誌格式化。
對於比較簡單的指令碼,可以直接使用basicConfig在程式碼中配置日誌;對於比較複雜的專案,可以將日誌儲存到一個配置檔案中,然後在程式碼中使用fileConfig函式讀取配置檔案。
例子:
1
[root@localhost ~]# vim test_logging.py 
2
3
#!/usr/local/bin/python
4
#coding:utf-8
5
import logging
6
7
logging.basicConfig(filename='app.log',level=logging.debug,format='%(asctime)s:%(levelname)s:%(message)s')
8
9
logging.debug('debug message')
10
logging.info('info message')
11
logging.warn('warn message')
12
logging.error('error message')
13
logging.critical('critical message')
14

對於複雜的專案,一般將日誌配置儲存到配置檔案中,例如下面是一個典型的配置檔案:
1
[root@localhost ~]# cat logging.cnf 
2
[loggers]
3
keys = root
4
5
[handlers]
6
keys = logfile
7
8
[formatters]
9
keys = generic
10
11
#定義root這個logger所使用的handler
12
[logger_root]
13
handlers = logfile
14
15
#定義handler輸出日誌的方式、日誌檔案的切換時間等
16
[handler_logfile]
17
class = handlers.TimedRotatingFileHandler
18
args = ('app.log','midnight',1,10)
19
level = DEBUG
20
formatter = generic
21
22
#定義日誌的格式,包括日誌產生的時間、日誌的級別、產生日誌的檔名和行號等資訊
23
[formatter_generic]
24
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s] %(message)s
25
有了配置檔案後,在python程式碼中使用logging.config模組的fileConfig函式載入日誌配置,如下所示:
1
[root@localhost ~]# cat test_logging.py 
2
#!/usr/local/bin/python
3
#coding:utf-8
4
import logging
5
import logging.config
6
7
logging.config.fileConfig('logging.cnf')
8
9
logging.debug('debug message')
10
logging.info('info message')
11
logging.warn('warn message')
12
logging.error('error message')
13
logging.critical('critical message')
14

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28916011/viewspace-2149515/,如需轉載,請註明出處,否則將追究法律責任。

相關文章