Linux -> Linux下的Python指令碼編程

來源:互聯網
上載者:User
Linux -> Linux下的Python指令碼編程 

撰寫Linux使用的
Pythonscript

這篇文章寫於兩年前,主題鎖定在以Python寫Linux的script。討論了Python script.的慣用寫法、字串處理、字元編碼、檔案與目錄處理、呼叫外部程式,以及利用內建連結庫進行網路通訊。

 

1 Linux、指令稿與Python

對Linux來說,指令稿(script)是至為重要的部分。

在主要的Linux distribution中間,從系統的啟動到運作,都離不開shell指令稿撰寫。在我的主機上面執行一下:

$ ls /usr/bin/* /bin/* | wc -l

2585

$ file /usr/bin/* /bin/* | grep "shell script" | wc -l

267

看,可以找到267個shell指令稿程式,超過/usr/bin和/usr目錄下所有(程式)檔案的十分之一。這還只是shell指令稿的部分而已。

在一個像Linux這樣以檔案為操作導向的作業系統上面,script.的活躍是理所當然的事情。絕大部分的系統設定都以字串的形式寫在組態檔案裡面,而作業系統的執行期資訊也存在檔案系統之中(/proc);直接處理這些字串就能管理系統,用指令稿語言來進行自動化是非常合適的。

像Python這種指令稿語言因為開發快速的關係,能夠很快地製作出我們想要的系統管理功能出來。除了開發快速之外,Python也具有容易維護的特性。相比之下,Perl程式雖然可以寫得更短,但也更不容易看懂;shell指令稿則不是完整的開發環境。Python是撰寫系統管理指令稿的理想工具

2 Python指令稿的格式

Python指令稿與其它語言的指令稿的基本格式完全一樣,本身都是純文字檔案,而在檔案頭要以#!指定直譯程式的位置:

#!/usr/bin/python

print "Hello, world!"

這是我們上一期寫過的hello.py程式,不要忘記chmod a+x hello.py,如此便可以在指令行下執行這個指令稿:

$ ./hello.py

Hello, world

我們習慣上會給Python程式取個副檔名.py,但Linux的指令稿並不需要綴上副檔名;把hello.py改成hello,程式一樣會正常執行。

.py副檔名對Python仍有特別的意義,但只在撰寫Python模組的時候才有用處。

對於指定Python直譯器標題,我們一般有兩種作法。

像以上的hello.py這種寫為絕對路徑的方式其實並非必要,我們可以改用相對路徑的方式來指定:

#!/usr/bin/env python

於是會以/usr/bin/env程式來叫用python直譯器,處理Python程式檔案。這麼作的好處是當系統中安裝有許多個不同的Python直譯器時,會採用路徑在最前面的那一個。如此一來,若使用者另外安裝了一版Python (例如裝在自己的家目錄),又把自己的Python放到路徑設定(PATH環境變數)的最前面,即會採用使用者自己安裝的Python。

每一版Python除了有python這個執行檔之外,還會附有內容完全相同的pythonX.Y這個執行檔,X.Y是該版Python的major version和minor version。譬如Python 2.3就會有python和python2.3這兩個直譯器,用起來是完全一樣的。如果我們寫的指令稿程式必須要使用某一個版本的Python,可以偷偷在指令稿標題上動手腳來進行限制;以Python 2.3為例,就把標題寫成:

#!/usr/bin/env python2.3

Note

Python提供了一套正統的方法來檢查所使用Python及所有相關環境的資訊。在指令稿標題上動手腳雖然方便,但不是保險的正統作法;只是,若程式本身就沒多長(譬如說二三十行),的確不必浪費時間去寫一串檢查程式。

當指令稿只使用了主流版號的標準連結庫時(這是一般的狀況),通常就不必指定Python的版本。

若寫成hello.py裡那種絕對路徑的標題,就會限定使用安裝在某一個位置的Python。

通常我們都會指定在/usr/bin/python或/usr/bin/pythonX.Y (看要指定哪一版),即系統所安裝的Python;寫成這樣的話,使用者就不好改用自己安裝的版本了。

Python直譯器還會讀取另一組格式為# -*- setting -*-的標題(通常接在第一行以後),其中常用的是:

# -*- coding: UTF-8 -*-

用途是指定「指令稿檔案內純文字的字元編碼(為UTF-8)」。如果你想要寫中文批註,這就非常重要;Python自己有一套字元編碼轉換的機制,實作在codecs模組裡面,但直到Python 2.4之前,繁體中文常用的Big5編碼並未進入標準的codecs模組。如果指令稿檔案使用了Python看不懂的字元編碼(就是指華文世界用的Big5和GB),程式雖然仍可執行,但Python直譯器會送出警告。如果想用中文撰寫批註,最好把指令稿檔案轉為UTF-8 Unicode,並如上指定編碼。

上一期已經提過了,Python也是以#當作單行批註符號的(和shell script.一樣);所有在這個符號之後的文字都是批註。

順帶一提,如果你習慣以VIM編輯Python指令稿,可以在檔案尾加上VIM的設定字串:

# vim:set nu et ts=4 sw=4 cino=>4:

設定顯示行號(nu)、展開跳格鍵(et,對Python程式來說,跳格鍵Tab是最要不得的東西),指定跳格字元為4 (ts=4)、位移字元寬為4 (sw=4)、C式縮排為>4 (cino=>4);然後再開啟文法標示(syntax highlighting,這個在.vimrc裡設定比較合適)。

使用這樣的編輯環境,對撰寫Python程式來說會很方便。

Python直譯器會依出現順序來執行程式代碼檔案裡的指令。如果我們想撰寫比較具組織性的指令稿,可以把平鋪直述的:

print "some operations"

改成這樣的程式碼結構:

def main():

print "some operations"

if __name__ == '__main__':

main()

亦即自行製作一個「進入點」main()函式。當指令稿比較長(超過一百行以上),以及將來在擴充指令稿的時候,就會比較方便。

總結來說,一個Python指令稿的常見格式應為:

#!/usr/bin/env python

# -*- coding: UTF-8 -*-

def main():

print "Hello, world"

if __name__ == '__main__':

main()

# vim:set nu et ts=4 sw=4 cino=>4:

3 字串處理

在管理Linux系統時,(純文字)設定檔案以及其中的字串處理是至為核心的部分;讓我們來看看Python如何進行這些工作。

因為我們在上一期已經用Python處理過字串和檔案了,所以在這裡,我們應該對字串處理作深入一點的介紹。

首先我們要知道的是,字串在Python裡面是一種對象。開啟Python互動式環境(到shell去執行python即可進入),執行以下動作:

>>> print type( "" )

<type 'str'>

>>> if type( "I am a string" ) is str: print True

...

True

>>> if type( "Another string" ) is str(): print True

...

type()是Python的內建函式,用來取得變數的型態。我們可以從這三個指令看出來,字串"", "I am a string"都是str類別的對象。

查看Python的線上檔案,會發現有兩組關於字串處理的連結庫;一組是string模組裡的函式,另一組則是字串對象專用的方法(String Methods)。兩者雖有一些差別,但功能的重複性相當高;我們討論的重點在字串方法。

我們常常會需要分析檔案中的字串:把字串拆解開來,依照給定的邏輯來判斷字串資料的意義。

因此,最常用的字串方法就是我們上一期有用到的split()

split()傳回的是列表(list),可以用索引值(以0起始)來存取列表中的各個項

再來示範一下:

>>>tokens = "This is a sample string used to demo split()".split()

>>>len(tokens)

9

>>> print tokens

['This', 'is', 'a', 'sample', 'string', 'used', 'to', 'demo', 'split()']

>>> print tokens[0], tokens[2]

This a

>>> print tokens[-1], tokens[-2]

split() demo

>>> print tokens[2:5]

['a', 'sample', 'string']

第一個指令把我們的字串切成了9個字串,存在tokens這個列表裡。

len()是個內建函式,用來量測像列表這種可以存放其它東西的對象的長度(傳回所包含的項目個數)。

列表只要是整數就可以了,但最大不能到項目個數;可以給入負值,表示從列表尾端開始計算。索引值-1即為列表的最後一個項目。

有辦法切開字串進行判斷了之後,我們常常還需要把分析結果給輸出出來,那麼就得接合字串;以字串的格式化操作(string format operations)就能完成這件工作。我們可以寫出以下的表示式:

>>>"%d %f %s" % (1, 1.2, "string")

'1 1.200000 string'

這就是字串格式化操作。以帶有特別轉換字元(conversion character)的格式化字串,後接%運運算元,再接一個tuple作為參數,就能把tuple裡的資料填進格式化字串裡去。常用的%d代表有號整數、%f代表浮點數、%s代表字串,完整的轉換字元表請參考Python的線上檔案。

Note

Python的tuple也是一種可以包含其它對象的資料結構,以整數索引存取其中的對象,但其行為與列表不盡相同。在文法上,tuple用(1, 2, 3)來宣告,而列表用[1, 2, 3]來宣告。如果tuple中只有一個對象,則要寫成(1,),不要忘記右括弧前的逗號,在字串格式化操作時,若轉換字元只有一個,%運算元後的tuple也可以用單一變數來代替。

字串對象另有一個叫作join()的方法可以用來結合字串,用法如下:

>>> "".join([ "a", "b", "c" ])

'abc'

>>> "-".join([ "a", "b", "c" ])

'a-b-c'

在處理字串時,最後要注意的是,Python的字串不可變。也就是說,想變更字串中的某一個字元,不能直接設:

>>> a = "write"

>>> a[2] = "o"

Traceback (most recent call last):

File "<stdin>", line 1, in ?

TypeError: object doesn't support item assignment

那是不合法的。那該怎麼辦呢?可以這樣作:

>>> print a[:2]+"o"+a[3:]

wrote

字串的內容雖然不能變更,但字串本身可以加起來(串接)。a[:2]表示取出a字串到索引2為止的部分;a[3:]表示取出a字串從索引3開始到結尾的部分;然後在中間接入"o"。最後我們還是可以得到wrote字串。這種操作索引的技巧,也可以用在一般的列表上。

Python同樣具有常規表示式(regular expression)的操作能力,實作在re模組裡面。用來執行字串取代是非常方便的。

3.1 轉換字元編碼

Python有一套處理字元編碼的codecs模組;我們以之即可自由地將字元轉換為各種不同的編碼,這是我們在處理多國語言資料時常需處理的問題。然而,字串對象本身就提供有encode()與decode()方法,我們不必匯入codecs模組就可以使用這兩個方法為我們提供的codecs能力。

此處我們得要注意一個事實,那就是Python擁有兩種字串對象。其一是我們剛剛一直在處理的str字串,而另一種呢,就是對多國語言處理非常重要的unicode字串。一般我們用引號或雙引號表示的都是普通的字串(str),而用u"string"表示的呢,就是unicode字串。decode()能把一般字元串解碼成unicode對象,而encode()則能把unicode對象編碼成各種支援的字元集。

在處理中文編碼之前,我們要為Python 2.3安裝相關的外加套件:cjkcodecs與iconvcodecs;前者是中日韓專用的codecs對象,而後者允許Python直接使用GNU iconv工具所提供的編碼,作為codecs對象。假設我們得把原本是Big5的編碼重編為UTF-8,那麼可以這樣作:

>>> f = open( "file.big5" )

>>> s = f.read()

>>> f.close()

>>> sp = s.decode('Big5').encode('UTF-8')

你可以在電腦上找一個內容是Big5編碼的檔案,把locale改成UTF-8,然後在Python互動式環境下執行以上的指令(該改的地方請改一下)。最後再用print s, sp比較一下轉換前後的字串。

4 檔案系統與目錄

在Linux系統中複製、搬移、刪除檔案與目錄也是管理時常見的動作。Python提供的os模組能處理作業系統所支援的大部分檔案系統操作,另外還有shutil模組,提供更高階的操作。

4.1 檔案系統操作

檔案系統與檔案內容是不一樣的議題。我們在進行檔案系統操作時,處理的是搬移(更名)、複製與刪除,比較沒有機會直接新增檔案。這些動作在os與shutil模組裡幾乎都有提供;我們應該先匯入這兩個模組。

若要複製檔案,我們可以這樣作:

>>> shutil.copy('data.txt', 'data.new.txt')

刪除檔案則用os.unlink():

>>> os.unlink('data.new.txt')

搬移(更名)有兩種方法

>>> os.rename('data.txt', 'data.alter.txt')

>>> shutil.move('data.alter.txt', 'data.txt')

第一種方法,若來源檔(第一個參數)與目的檔不在同一個檔案系統內(分割區),此動作可能會失效(不同的Unix有不同的處理方法)。

第二種方法比較高階,無論來源檔與目的檔是否在相同的檔案系統內,都可以使用。

4.2 路徑的處理

管理系統的時候多半不會只處理目前的目錄內的檔案,所以常要對路徑字串進行處理。os.path模組提供了處理路徑的函式,常用的有:

  • abspath():接受一個路徑字串,傳回該路徑所代表的絕對路徑。
  • realpath():接受一個路徑字串,計算該路徑中包含的符號連結(symbolic link),傳回所代表的真正路徑。
  • split(), dirname(), basename():

      split()接受一個路徑字串,從最後一個路徑項目前切開,分成包含該項目的目錄與該項目名本身,以tuple傳回。

      dirname()是split()傳回值的第一個元素;

      basename()是第二個元素。

  • join():接受一個路徑列表,把該列表中的每個元素接成一個完整路徑字串後傳回。
  • splitext():接受一個路徑字串,分開其副檔名,將主檔名與副檔名用一個tuple傳回。
  • exists():測試傳入的路徑字串是否存在,傳回布爾值。
  • isfile(), isdir(), islink(), isabs():分別用來測試所傳入的路徑字串是否為檔案、目錄、符號連結或絕對路徑;傳回布爾值。

實際要使用的時候,大概會像是這樣子:

>>> os.path.split( "a/b/c" )

('a/b', 'c')

>>> os.path.join( "a", "b", "c" )

'a/b/c'

>>> os.path.splitext( "dir/file.ext" )

('dir/file', '.ext')

你可以在你的目錄結構裡,用真正的路徑來試試看!

5 外部程式呼叫

許多在shell指令稿中要靠呼叫外部程式才能完成的作業,都能用Python的內建模組來完成,例如上面提到的字串處理、檔案處理、目錄處理等等。而若遇到Python不足的地方,或是有非常特別的操作,當然也可以呼叫外部的程式。

os模組有一個system()函式可以用來呼叫外部程式:

>>> os.system( 'ls' )

weekly20051204.doc

weekly20051211.doc

0

>>> 

最後顯示出來的0不是ls程式的輸出,而是其傳回值。

os.system()函式能進行最簡單的外部程式呼叫,不能對該程式的輸出入資料進一步處理;如果我們只想簡單執行程式,os.system()函式將是最佳的選擇

5.1 管線

當我們也需要對外部程式的輸出入資料進行處理的時候,os.system()就不夠用了。

Python另外有popen2模組,可以讓我們管理外部程式子行程的輸出入管線(pipe)。

在popen2模組裡有popen2(), popen3()和popen4()三個工具函式,分別會重導向子行程的標準輸出入、標準輸出入及錯誤輸出、標準輸出合并錯誤輸出及標準輸入。

簡單用範例來說明最常用的popen2() (別忘了先import popen2喔):

>>> stdout, stdin = popen2.popen2("ls")

>>> str = stdout.read()

>>> print ostr

weekly20051204.doc

weekly20051211.doc

>>> 

popen2.popen2()會傳回連結到ls程式輸出入的兩個檔案對象,我們取名為stdout與stdin。呼叫了popen2.popen2()之後,外部程式馬上就會執行,然後我們就能從stdout檔案對象裡讀出該外部程式的標準輸出資料了。如此一來,該程式的執行結果就不會直接顯示在終端機上,我們可以在Python裡面先處理過以後,再決定該怎麼辦。

如果我們想呼叫的程式也會進行錯誤輸出(stderr),而我們想要處理的話,就改用popen3()或popen4()函式。popen3()的錯誤輸出會串連至一個獨立的檔案對象,而popen4()則會把錯誤輸出一起放到標準輸出所連結的檔案對象裡;你可以視需要使用。

Note

在Python 2.4裡有一個新的subprocess模組,可以執行所有的外部程式呼叫功能。所以在Python 2.4裡不再需要os與popen2模組裡的相關函式了;當然,舊模組不會消失,所以在Python 2.4裡還是可以用popen2,我們的舊程式不會出問題。

6 網際網路通訊

Python內建的連結庫裡就具備相當方便的網際網路通訊功能,不必呼叫外部程式。

網際網路通訊是個大範圍,其中最常用到的大概數全球資訊網了;我們舉Zope應用程式服務器來作例子。Zope使用ZODB對象資料庫來儲存資料,這個系統會把存取動作紀錄下來,當使用者刪除其中的資料時,資料不會實際刪除,要等到手動壓縮(pack)資料庫的時候,才會真正把資料刪除。這個壓縮功能的動作選項是放在web-based的ZMI裡面,沒有指令行介面;如果我們不想手動連進ZMI來執行壓縮,就得寫一個能進行HTTP操作的指令稿。

我們要寫的程式應該具有以下的命令列介面:

packzope.py -u<URL of Zope server> -d<day>-U<username> -P<password>

這個packzope.py程式要負責用HTTP和伺服器溝通,把從命令列取得的使用者名稱和密碼提供給Zope伺服器,並且用GET方法把要壓縮的天數(捨棄指定天數前的資料)告訴Zope伺服器。以下是寫好的程式:

#!/usr/bin/env python

import sys

import urllib

class parameters:

def __init__(self):

from optparse import OptionParser, OptionGroup

op = OptionParser(

usage = "usage: %prog -u URL -d DAYS -U USERNAME -P PASSWORD",

version = "%prog, " + "%s" % __revision__

)

op.add_option("-u", action="store", type="string", /

dest="url", /

help="URL of site to open"

)

op.add_option("-d", action="store", type="int", /

dest="days", default=1, /

help="erase days before"

)

op.add_option("-U", action="store", type="string", /

dest="username", /

help="username"

)

op.add_option("-P", action="store", type="string", /

dest="password", /

help="password"

)

self.op = op

(self.options, self.args) = self.op.parse_args()

params = parameters()

if not params.options.url or /

not params.options.username or /

not params.options.password :

params.op.print_help()

sys.exit(1)

url = "%s/Control_Panel/Database/manage_pack?days:float=%d" % /

(params.options.url, params.options.days)

def main(): try: f = MyOpener().open(url).read() print "Successfully packed ZODB on host %s" % params.options.url except: print "Cannot open URL %s, aborted" % url raiseif __name__ == '__main__': main()

程式前半段在處理命令列參數 (classparameters),而在 main()函式裡實際進行連線動作。packzope.py利用
urllib模組來連結 Zope 伺服器,並利用 subclassing urllib.FancyURLopener類別來自訂使用者名稱與密碼的輸入。壓縮完畢之後,程式會輸出以下的字樣:

Successfully packed ZODB on host http://someplace:port

我們可以把 packzope.py放到 crontab 裡定期執行。這就是一種自動化網路操作。

7 結語

本文藉由討論以 Python 進行 Linux 操作自動化的技巧,對 Python 的應用作了進一步的介紹。當然,在進行任何種類的 Python 程式開發時,都可以參考 Python 的線上說明檔案。Dive into Python 是一本容易上手的自由 Python 書籍,你也可以在網路上找到中文譯本。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.