本章介介紹了shutil,zipfile模塊的使用,我們先來認識一下這2個模塊吧。
一.shutil模塊
shutil模塊主要用於對文件或文件夾進行處理,包括:復制,移動,改名和刪除文件,在shutil模塊中主要以下這么幾個函數:
1.復制文件和文件夾
shutil模塊提供了2個函數:shutil.copy()和shutil.copytree()
shutil.copy的語法格式:
copy(src, dst)
作用:
將src處的文件復制到dst路徑中去,其中src,dst都是字符串形式的路徑。如果dst是一個文件名,它將作為被復制文件的新名字,相當於把原路徑的文件復制到新路徑並改名。
舉例:
將/etc/my.cnf 復制到/root/mysql中
In [12]: import shutil In [13]: shutil.copy('/etc/my.cnf','/root/mysql/') Out[13]: '/root/mysql/my.cnf' In [15]: ll /root/mysql/ total 4
-rw-r--r--. 1 root 960 Apr 22 16:57 my.cnf
將/etc/my.cnf復制到/root/mysql並改名為mysql.cnf
In [16]: shutil.copy('/etc/my.cnf','/root/mysql/mysql.cnf') Out[16]: '/root/mysql/mysql.cnf' In [17]: ll /root/mysql/ total 8
-rw-r--r--. 1 root 960 Apr 22 16:57 my.cnf -rw-r--r--. 1 root 960 Apr 22 17:00 mysql.cnf
shutil.copytree的語法格式:
copytree(src, dst)
作用:
復制整個文件夾。將src處的文件夾,包括它的所有文件和子文件夾,復制到路徑dst處的文件夾。返回一個新復制的文件夾路徑的字符串。
舉例:
In [20]: shutil.copytree('/etc/yum.repos.d','/root/repo.back') Out[20]: '/root/repo.back' In [21]: ll /root/drwxr-xr-x. 2 root 4096 Apr 23 2017 repo.back/ drwxr-xr-x. 2 root 4096 Apr 23 2017 repo.bak/
注意:dst必須是一個系統中不存在目錄,不然會報錯:
In [20]: shutil.copytree('/etc/yum.repos.d','/root/repo.bak')
FileExistsError: [Errno 17] File exists: '/root/repo.bak'
2.文件和文件夾的移動和改名
shutil.move()
語法格式:
move(stc,dst)
作用:
將路徑stc處的文件夾移動到路徑dst,並返回新位置的絕對路徑的字符串。
舉例:
將/root/目錄下的a.txt移動到/root/test/目錄中
In [26]: shutil.move('/root/a.txt','/root/test/') Out[26]: '/root/test/a.txt' In [27]: ll /root/test total 4
-rw-r--r--. 1 root 6 Apr 22 17:30 a.txt
注意:
如果dst指向一個文件夾,src文件將移動到dst中,並保持原來的文件名,前提是dst必須是系統中已經存在的目錄。
如果目標文件中已存在相同名稱的文件將被覆蓋,需要注意。
3.刪除文件和文件夾
在os模塊中:
os.remove(path)可以刪除一個文件
os.rmdir(path)可以刪除一個空文件夾。
在shutil模塊中:
shutil.rmtree(path)可以刪除一個文件夾及其所有的內容。
語法格式:
os.rmdir(path) shutil.rmtree(path)
舉例:
1 In [34]: os.remove('/root/test/a.txt') 2
3 In [35]: ll /root/test/
4 total 0
5
6 In [36]: shutil.move('/root/CentOS-Base.repo','/root/test/') 7 Out[36]: '/root/test/CentOS-Base.repo'
8
9 In [37]: ll test 10 total 4
11 -rw-r--r--. 1 root 2573 Apr 23 2017 CentOS-Base.repo 12
13 In [38]: shutil.rmtree('/root/test') 14
15 In [39]: ll 16 total 12
17 -rw-------. 1 root 1468 Apr 16 21:03 anaconda-ks.cfg 18 drwxr-xr-x. 2 root 57 Apr 23 2017 download/
19 drwxr-xr-x. 2 root 37 Apr 22 17:00 mysql/
20 drwxr-xr-x. 6 root 95 Apr 16 21:58 py34/
21 drwxr-xr-x. 3 root 18 Apr 16 23:01 python/
22 drwxr-xr-x. 2 root 4096 Apr 23 2017 repo.back/
23 drwxr-xr-x. 2 root 4096 Apr 23 2017 repo.bak/
注意:
以上的刪除都是永久的刪除。為了安全起見最好使用send2trash第三方模塊,它會將刪除的文件放入回收站。在python3中已集成了這個模塊。
send2trash用法:
import send2trash
send2trash(path)
二.遍歷目錄樹
對文件的處理,尤其是批量操作就不得不對目錄進行遍歷。在python中os模塊中的os.walk()函數就可以做到。
這個函數會遞歸遍歷指定目錄及子目錄,返回一個3元組信息:當前目錄名,子目錄名,文件名,不包括 . 和 ..
常見用法:
#!/usr/bin/env python3.4 #coding:utf-8 import os for foldName,subfolders,filenames in os.walk('/root/'): print('The current folder is: ' + foldName) for subfolder in subfolders: print('subfolder of ' + foldName + ':' + subfolder) for filename in filenames: print('file inside ' + foldName + ':' + filename) print('')
三.實踐項目參考答案
1 #!/usr/bin/env python3.4
2 # coding:utf-8
3 import os 4 import shutil 5 import send2trash 6
7 # 9.8.1
8 # 拷貝指定格式文件到指定目錄,下面程序是將/etc目錄下所的.conf文件拷貝到/root/test/目錄里。
9 src = '/etc/'
10 dst = '/root/test/'
11 ftype = '.conf'
12 count = 0 13 for filename in os.listdir(src): 14 if filename.endswith(ftype): 15 shutil.copy(src + filename,dst) 16 count += 1
17 print('文件 ' + src + filename + '\t被拷貝到---> ' + dst + ' 目錄下') 18 print("該目錄下所有的 " + ftype + "文件已被拷貝到" + dst + "目錄下") 19 print('共拷貝了 ' + str(count) + ' 個文件') 20
21 # 9.8.2
22 # 搜索指定目錄下大於100M的文件,打印出來並刪除
23 # 可以手動創建一個指定大小的空文件做試驗
24 # dd if=/dev/zero of=hello.txt bs=100M count=1
25 for foldname,subfolders,filenames in os.walk(dst): 26 for files in filenames: 27 if os.path.getsize(dst + files) / 1024 /1024 > 100: 28 print('大於100M的文件有:' + files + ' ' + str(os.path.getsize(dst + files) / 1024 / 1024) +'Mb') 29 send2trash.send2trash(dst + files)
9.8.3
假設test文件夾下有如下文件,文件是以spam開頭加上數字編號,但是編號並不連續有缺失,而且有的並不包含數字,我們需要找出不符合文件名的文件並重新命名成連續編號的文件名。
(py34) [root@master test]# ls
spam002.txt spam004.txt spam006.txt spam008.txt spam999.txt spam003.txt spam005.txt spam007.txt spam011.txt spamkkdf.txt
參考代碼如下:
1 #!/usr/bin/env python3.4
2 # coding:utf-8
3 import re 4 import os 5 fdir = '/root/python/py-9/test/'
6 fdir_list = os.listdir(fdir) 7 fdir_count = len(fdir_list) 8 print(fdir_list) 9 print('該目錄下共有 %d 個文件' %fdir_count) 10 f_pre = 'spam'
11 f_num = [] 12 f_end = '.txt'
13 fs_list = [] 14 # 這里只假定文件數量小100的情況
15 for i in range(1,fdir_count + 1): 16 if i < 10: 17 f_name = f_pre + '00' + str(i) + f_end 18 f_num.append('00' + str(i)) 19 fs_list.append(f_name) 20 else: 21 f_name = f_pre + '0' + str(i) + f_end 22 f_num.append('0' + str(i)) 23 fs_list.append(f_name) 24 max_f_num = max(f_num) 25 print('該目錄下文件最大的編號應該是: %s' %max_f_num) 26 print('正確的文件名應該是:') 27 print(fs_list) 28
29 # 使用正則表達式搜索目錄中已有編號的文件並存入列表yf_num中
30 re_num = '\d{3}'
31 yf_num = re.findall(f_pre + re_num + f_end,' '.join(fdir_list)) 32 ra_num = re.findall(re_num,' '.join(fdir_list)) 33 print('目錄中已有編號文件:\n%s' %yf_num) 34
35 # fq_list為目錄中缺失編號的文件名列表
36 # fx_list為當前目錄中需要修改名稱的文件列表
37 fq_list = [] 38 fx_list = [] 39 # 定位缺失的編號文件並放入列表中
40 for a in fs_list: 41 if a not in yf_num: 42 fq_list.append(a) 43 print('缺少的文件編號是:\n%s' %fq_list) 44
45 # 查找目錄中沒有編號或不正連續的編號文件並放入列表中
46 for f_rename in fdir_list: 47 if f_rename not in fs_list: 48 fx_list.append(f_rename) 49 print('需要修改的文件名有:\n%s' %fx_list) 50
51 # 更改文件名
52 for k in fq_list: 53 for v in fx_list: 54 os.rename(fdir + v,fdir + k) 55 # 每當修完一個文件名應該更新一下這個列表
56 fx_list.remove(v) 57 print('改完名后的結果為:') 58 os.system('ls')
今天回來看看,決定用函數的方式來練習並實現,代碼有了一些小的改進,上代碼:
1 #!/usr/bin/env python3.4
2 # coding:utf-8
3 import re 4 import os 5 fdir = '/root/github/shell/python3/py-9/test/'
6 fdir_list = os.listdir(fdir) 7 fdir_f_count = len(fdir_list) 8 print('當前目錄的文件為:\n%s' % fdir_list) 9 def getFileformat(f_pre,f_num,f_end): 10 fileformat = f_pre + f_num + f_end 11 return fileformat 12 file_list = [] 13 def getTruefile(): 14 for i in range(1,fdir_f_count + 1): 15 if i < 10: 16 f_format = getFileformat('spam','00' + str(i),'.txt') 17 file_list.append(f_format) 18 else: 19 f_format = getFileformat('spam','0' + str(i),'.txt') 20 file_list.append(f_format) 21 return file_list 22 truefilelist = getTruefile() 23 print('正確的文件編號應該是:\n%s' %truefilelist) 24 lostnumfilelist = [] 25 def getLostnumfile(fdirlist,truelist): 26 for lf in truelist: 27 if lf not in fdirlist: 28 lostnumfilelist.append(lf) 29 return lostnumfilelist 30 lostnumfile = getLostnumfile(fdir_list,file_list) 31 print('缺失的文件編號為:\n%s' %lostnumfile) 32
33 renamelist = [] 34 def getrenamefile(fdirlist,func): 35 for a in fdirlist: 36 if a not in func: 37 renamelist.append(a) 38 return renamelist 39 renamefilelist = getrenamefile(fdir_list,file_list) 40 print('需要修改的文件是\n%s' %renamefilelist) 41
42 def renamefile(func1,func2): 43 for b in func1: 44 for c in func2: 45 os.rename(fdir + c,fdir + b) 46 func2.remove(c) 47 rename = renamefile(lostnumfile,renamefilelist) 48 os.chdir(fdir) 49 print('修改后的結果為:') 50 os.system('ls')