作為一個初學shell的程式員來說,使用shell的一些命令調用,比如就寫幾行,做一些自動化的操作,簡單又實用,但是涉及邏輯控制和
字串處理時,看到那if/fi,case/esac……還有古離古怪的布爾判斷……還有複雜而又名字很響而且很老資格的sed、awk……我就想,
在沒有非要在shell中寫的命令的時候,幹嘛不用python呢?
當真有非要在shell中寫的命令的時候, shell和python合起來也很HI呀,想想1K行的shell和1K行的python讀起來、寫起來、調試起來、維護起來有什麼區別。
QUOTE:
應用python編寫shell指令碼
今天同事叫我編寫一個shell指令碼。話說,雖然我受*nix的影響甚深,但是對於*nix裡隨處可見的sh指令碼卻是討厭之極。為什麼討厭呢?首先 是因為sh指令碼那莫名其妙的文法,感覺就像隨寫隨扔的程式,完全沒有任何美感可言。其次是sh指令碼的處理能力還是比較弱的,在文本處理、XML處理還有網 絡編程方面,基本上都要藉助於perl,awk等一堆程式。我對這些程式也是不大喜歡的,況且學習第三方軟體總要時間,還不如都用python好了。
那,python可以做shell指令碼嗎? 首先介紹一個函數:
os.system(command)
這個函數可以調用shell運行命令列command並且返回它的傳回值。試一下在python的解譯器裡輸入os.system(”ls -l”),就可以看到”ls”列出了目前的目錄下的檔案。可以說,通過這個函數,python就擁有了shell的所有能力。呵呵。。不過,通常這條命令不 需要用到。因為shell常用的那些命令在python中通常有對應而且同樣簡潔的寫法。
shell中最常用的是ls命令,python對應的寫法是:os.listdir(dirname),這個函數返回字串列表,裡面是所有的檔案名稱,不過不包含”.”和”..”。如果要遍曆整個目錄的話就會比較複雜一點。我們等下再說吧。先在解譯器裡試一下:
>>> os.listdir(”/”)
[’tmp’, ‘misc’, ‘opt’, ‘root’, ‘.autorelabel’, ’sbin’, ’srv’, ‘.autofsck’, ‘mnt’, ‘usr’, ‘var’, ‘etc’, ’selinux’, ‘lib’, ‘net’, ‘lost+found’, ’sys’, ‘media’, ‘dev’, ‘proc’, ‘boot’, ‘home’, ‘bin’]
就像這樣,接下去所有命令都可以在python的解譯器裡直接運行觀看結果。
對應於cp命令的是:shutil.copy(src,dest),這個函數有兩個參數,參數src是指源檔案的名字,參數dest則是目標檔案或 者目標目錄的名字。 如果dest是一個目錄名,就會在那個目錄下建立一個相同名字的檔案。與shutil.copy函數相類似的是 shutil.copy2(src,dest),不過copy2還會複製最後存取時間和最後更新時間。
不過,shell的cp命令還可以複製目錄,python的shutil.copy卻不行,第一個參數只能是一個檔案。這怎麼辦?其 實,python還有個shutil.copytree(src,dst[,symlinks]) 。參數多了一個symlinks,它是一個布爾值,如果是True的話就建立符號連結。
移動或者重新命名檔案和目錄呢?估計被聰明的朋友猜到了,shutil.move(src,dst),呵呵。。與mv命令類似,如果src和dst在 同一個檔案系統上,shutil.move只是簡單改一下名字,如果src和dst在不同的檔案系統上,shutil.move會先把src複製到 dst,然後刪除src檔案。看到現在,大多數朋友應該已經對python的能力有點眉目了,接下來我就列個表,介紹一下其它的函數:
os.chdir(dirname)
把當前工作目錄切換到dirname下
os.getcwd()
返回當前的工作目錄路徑
os.chroot(dirname)
把dirname作為進程的根目錄。和*nix下的chroot命令類似
os.chmod(path,mode)
更改path的許可權位。mode可以是以下值(使用or)的組合:
os.S_ISUID
os.S_ISGID
os.S_ENFMT
os.S_ISVTX
os.S_IREAD
os.S_IWRITE
os.S_IEXEC
os.S_IRWXU
os.S_IRUSR
os.S_IWUSR
os.S_IXUSR
os.S_IRWXG
os.S_IRGRP
os.S_IWGRP
os.S_IXGRP
os.S_IRWXO
os.S_IROTH
os.S_IWOTH
os.S_IXOTH
具體它們是什麼含義,就不仔細說了,基本上就是R代表讀,W代表寫,X代表執行許可權。USR代表使用者,GRP代表組,OTH代表其它。
os.chown(path,uid,gid)
改變檔案的屬主。uid和gid為-1的時候不改變原來的屬主。
os.link(src,dst)
建立硬串連
os.mkdir(path,[mode])
建立目錄。mode的意義參見os.chmod(),預設是0777
os.makedirs(path,[mode])
和os.mkdir()類似,不過會先建立不存在的父目錄。
os.readlink(path)
返回path這個符號連結所指向的路徑
os.remove(path)
刪除檔案,不能用於刪除目錄
os.rmdir(path)
刪除檔案夾,不能用於刪除檔案
os.symlink(src,dst)
建立符號連結
shutil.rmtree(path[,ignore_errors[,onerror]])
刪除檔案夾
介紹了這麼多,其實只要查一下os和shutil兩個模組的文檔就有了,呵呵。。真正編寫shell指令碼的時候還需要注意:
1.環境變數。python的環境變數儲存在os.environ這個字典裡,可以用普通字典的方法修改它,使用system啟動其它程式的時候會自動被繼承。比如:
os.environ[”fish”]=”nothing”
不過也要注意,環境變數的值只能是字串。和shell有些不同的是,python沒有export環境變數這個概念。為什麼沒有呢?因為python沒有必要有:-)
2.os.path這個模組裡包含了很多關於路徑名處理的函數。在shell裡路徑名處理好像不是很重要,但是在python裡經常需要用到。最常用的兩個是分離和合并目錄名和檔案名稱:
os.path.split(path) -> (dirname,basename)
這個函數會把一個路徑分離為兩部分,比如:os.path.split(”/foo/bar.dat”)會返回(”/foo”,”bar.dat”)
os.path.join(dirname,basename)
這個函數會把目錄名和檔案名稱組合成一個完整的路徑名,比如:os.path.join(”/foo”,”bar.dat”)會返回”/foo/bar.dat”。這個函數和os.path.split()剛好相反。
還有這些函數:
os.path.abspath(path)
把path轉成絕對路徑
os.path.expanduser(path)
把path中包含的”~”和”~user”轉換成使用者目錄
os.path.expandvars(path)
根據環境變數的值替換path中包含的”$name”和”${name}”,比如環境變數FISH=nothing,那os.path.expandvars(”$FISH/abc”)會返回”nothing/abc”
os.path.normpath(path)
去掉path中包含的”.”和”..”
os.path.splitext(path)
把path分離成基本名和副檔名。比如:os.path.splitext(”/foo/bar.tar.bz2″)返回(’/foo/bar.tar’, ‘.bz2′)。要注意它和os.path.split()的區別
3.在os模組有一個很好用的函數叫os.stat()沒有介紹,因為os.path模組裡包含了一組和它具有同樣功能的函數,但是名字更好記一點。
os.path.exists(path)
判斷檔案或者目錄是否存在
os.path.isfile(path)
判斷path所指向的是否是一個普通檔案,而不是目錄
os.path.isdir(path)
判斷path所指向的是否是一個目錄,而不是普通檔案
os.path.islink(path)
判斷path所指向的是否是一個符號連結
os.path.ismount(path)
判斷path所指向的是否是一個掛接點(mount point)
os.path.getatime(path)
返回path所指向的檔案或者目錄的最後存取時間。
os.path.getmtime(path)
返回path所指向的檔案或者目錄的最後修改時間
os.path.getctime(path)
返回path所指向的檔案的建立時間
os.path.getsize(path)
返回path所指向的檔案的大小
4.應用python編寫shell指令碼經常要用到os,shutil,glob(Regex的檔案名稱),tempfile(臨時文 件),pwd(操作/etc/passwd檔案),grp(操作/etc/group檔案),commands(取得一個命令的輸出)。前面兩個已經基本 上介紹完了,後面幾個很簡單,看一下文檔就可以了。
5.sys.argv是一個列表,儲存了python程式的命令列參數。其中sys.argv[0]是程式本身的名字。
不能光說不練,接下來我們就編寫一個用於複製檔案的簡單指令碼。前兩天叫我寫指令碼的同事有個幾萬個檔案的目錄,他想複製這些檔案到其它的目錄,又不能 直接複製目錄本身。他試了一下”cp src/* dest/”結果報了一個命令列太長的錯誤,讓我幫他寫一個指令碼。操起python來:
import sys,os.path,shutil
for f in os.listdir(sys.argv[1]):
shutil.copy(os.path.join(sys.argv[1],f),sys.argv[2])
再試一下linuxapp版裡的文章——把一個檔案夾下的所有檔案重新命名成10001~10999。可以這樣寫:
import os.path,sys
dirname=sys.argv[1]
i=10001
for f in os.listdir(dirname):
src=os.path.join(dirname,f)
if os.path.isdir(src):
continue
os.rename(src,str(i))
i+=1