# 解決方案
當我們基本完成程序的設計,我們就可以編寫代碼了,它是對我們的解決方案的**實施**。
```
#!/usr/bin/python
# Filename: backup_ver1.py
import os
import time
# 1\. The files and directories to be backed up are specified in a list.
source = ['/home/swaroop/byte', '/home/swaroop/bin']
# If you are using Windows, use source = [r'C:\Documents', r'D:\Work'] or something like that
# 2\. The backup must be stored in a main backup directory
target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using
# 3\. The files are backed up into a zip file.
# 4\. The name of the zip archive is the current date and time
target = target_dir + time.strftime('%Y%m%d%H%M%S') + '.zip'
# 5\. We use the zip command (in Unix/Linux) to put the files in a zip archive
zip_command = "zip -qr '%s' %s" % (target, ' '.join(source))
# Run the backup
if os.system(zip_command) == 0:
????print 'Successful backup to', target
else:
????print 'Backup FAILED'
```
(源文件:[code/backup_ver1.py](code/backup_ver1.py))
## 輸出
```
$ python backup_ver1.py
Successful backup to /mnt/e/backup/20041208073244.zip
```
現在,我們已經處于**測試**環節了,在這個環節,我們測試我們的程序是否正確工作。如果它與我們所期望的不一樣,我們就得**調試**我們的程序,即消除程序中的 瑕疵 (錯誤)。
## 它如何工作
接下來你將看到我們如何把 設計 一步一步地轉換為 代碼 。
我們使用了`os`和`time`模塊,所以我們輸入它們。然后,我們在`source`列表中指定需要備份的文件和目錄。目標目錄是我們想要存儲備份文件的地方,它由`target_dir`變量指定。zip歸檔的名稱是目前的日期和時間,我們使用`time.strftime()`函數獲得。它還包括`.zip`擴展名,將被保存在`target_dir`目錄中。
`time.strftime()`函數需要我們在上面的程序中使用的那種定制。`%Y`會被無世紀的年份所替代。`%m`會被01到12之間的一個十進制月份數替代,其他依次類推。這些定制的詳細情況可以在《Python參考手冊》中獲得。《Python參考手冊》包含在你的Python發行版中。注意這些定制與用于`print`語句的定制(`%`后跟一個元組)類似(但不完全相同)
我們使用加法操作符來 級連 字符串,即把兩個字符串連接在一起返回一個新的字符串。通過這種方式,我們創建了目標zip文件的名稱。接著我們創建了`zip_command`字符串,它包含我們將要執行的命令。你可以在shell(Linux終端或者DOS提示符)中運行它,以檢驗它是否工作。
**zip**命令有一些選項和參數。`-q`選項用來表示zip命令**安靜地**工作。`-r`選項表示zip命令對目錄**遞歸地**工作,即它包括子目錄以及子目錄中的文件。兩個選項可以組合成縮寫形式`-qr`。選項后面跟著待創建的zip歸檔的名稱,然后再是待備份的文件和目錄列表。我們使用已經學習過的字符串`join`方法把`source`列表轉換為字符串。
最后,我們使用`os.system`函數 運行 命令,利用這個函數就好像在 系統 中運行命令一樣。即在shell中運行命令——如果命令成功運行,它返回0,否則它返回錯誤號。
根據命令的輸出,我們打印對應的消息,顯示備份是否創建成功。好了,就是這樣我們已經創建了一個腳本來對我們的重要文件做備份!
給Windows用戶的注釋
你可以把`source`列表和`target`目錄設置成任何文件和目錄名,但是在Windows中你得小心一些。問題是Windows把反斜杠(\)作為目錄分隔符,而Python用反斜杠表示轉義符!
所以,你得使用轉義符來表示反斜杠本身或者使用自然字符串。例如,使用`'C:\\Documents'`或`r'C:\Documents'`而**不是**`'C:\Documents'`——你在使用一個不知名的轉義符`\D`!
現在我們已經有了一個可以工作的備份腳本,我們可以在任何我們想要建立文件備份的時候使用它。建議Linux/Unix用戶使用前面介紹的[可執行的方法](ch03s05.html),這樣就可以在任何地方任何時候運行備份腳本了。這被稱為軟件的**實施**環節或**開發**環節。
上面的程序可以正確工作,但是(通常)第一個程序并不是與你所期望的完全一樣。例如,可能有些問題你沒有設計恰當,又或者你在輸入代碼的時候發生了一點錯誤,等等。正常情況下,你應該回到設計環節或者調試程序。
第一個版本的腳本可以工作。然而,我們可以對它做些優化以便讓它在我們的日常工作中變得更好。這稱為軟件的**維護**環節。
我認為優化之一是采用更好的文件名機制——使用 時間 作為文件名,而當前的 日期 作為目錄名,存放在主備份目錄中。這樣做的一個優勢是你的備份會以等級結構存儲,因此它就更加容易管理了。另外一個優勢是文件名的長度也可以變短。還有一個優勢是采用各自獨立的文件夾可以幫助你方便地檢驗你是否在每一天創建了備份,因為只有在你創建了備份,才會出現那天的目錄。
```
#!/usr/bin/python
# Filename: backup_ver2.py
import os
import time
# 1\. The files and directories to be backed up are specified in a list.
source = ['/home/swaroop/byte', '/home/swaroop/bin']
# If you are using Windows, use source = [r'C:\Documents', r'D:\Work'] or something like that
# 2\. The backup must be stored in a main backup directory
target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using
# 3\. The files are backed up into a zip file.
# 4\. The current day is the name of the subdirectory in the main directory
today = target_dir + time.strftime('%Y%m%d')
# The current time is the name of the zip archive
now = time.strftime('%H%M%S')
# Create the subdirectory if it isn't already there
if not os.path.exists(today):
????os.mkdir(today) # make directory
????print 'Successfully created directory', today
# The name of the zip file
target = today + os.sep + now + '.zip'
# 5\. We use the zip command (in Unix/Linux) to put the files in a zip archive
zip_command = "zip -qr '%s' %s" % (target, ' '.join(source))
# Run the backup
if os.system(zip_command) == 0:
????print 'Successful backup to', target
else:
????print 'Backup FAILED'
```
(源文件:[code/backup_ver2.py](code/backup_ver2.py))
## 輸出
```
$ python backup_ver2.py
Successfully created directory /mnt/e/backup/20041208
Successful backup to /mnt/e/backup/20041208/080020.zip
$ python backup_ver2.py
Successful backup to /mnt/e/backup/20041208/080428.zip
```
## 它如何工作
兩個程序的大部分是相同的。改變的部分主要是使用`os.exists`函數檢驗在主備份目錄中是否有以當前日期作為名稱的目錄。如果沒有,我們使用`os.mkdir`函數創建。
注意`os.sep`變量的用法——這會根據你的操作系統給出目錄分隔符,即在Linux、Unix下它是`'/'`,在Windows下它是`'\\'`,而在Mac OS下它是`':'`。使用`os.sep`而非直接使用字符,會使我們的程序具有移植性,可以在上述這些系統下工作。
第二個版本在我做較多備份的時候還工作得不錯,但是如果有極多備份的時候,我發現要區分每個備份是干什么的,會變得十分困難!例如,我可能對程序或者演講稿做了一些重要的改變,于是我想要把這些改變與zip歸檔的名稱聯系起來。這可以通過在zip歸檔名上附帶一個用戶提供的注釋來方便地實現。
```
#!/usr/bin/python
# Filename: backup_ver3.py
import os
import time
# 1\. The files and directories to be backed up are specified in a list.
source = ['/home/swaroop/byte', '/home/swaroop/bin']
# If you are using Windows, use source = [r'C:\Documents', r'D:\Work'] or something like that
# 2\. The backup must be stored in a main backup directory
target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using
# 3\. The files are backed up into a zip file.
# 4\. The current day is the name of the subdirectory in the main directory
today = target_dir + time.strftime('%Y%m%d')
# The current time is the name of the zip archive
now = time.strftime('%H%M%S')
# Take a comment from the user to create the name of the zip file
comment = raw_input('Enter a comment --> ')
if len(comment) == 0: # check if a comment was entered
????target = today + os.sep + now + '.zip'
else:
????target = today + os.sep + now + '_' +
????????comment.replace(' ', '_') + '.zip'
# Create the subdirectory if it isn't already there
if not os.path.exists(today):
????os.mkdir(today) # make directory
????print 'Successfully created directory', today
# 5\. We use the zip command (in Unix/Linux) to put the files in a zip archive
zip_command = "zip -qr '%s' %s" % (target, ' '.join(source))
# Run the backup
if os.system(zip_command) == 0:
????print 'Successful backup to', target
else:
????print 'Backup FAILED'
```
(源文件:[code/backup_ver3.py](code/backup_ver3.py))
## 輸出
```
$ python backup_ver3.py
File "backup_ver3.py", line 25
target = today + os.sep + now + '_' +
????????????????????????????????^
SyntaxError: invalid syntax
```
## 它如何(不)工作
**這個程序不工作!**Python說有一個語法錯誤,這意味著腳本不滿足Python可以識別的結構。當我們觀察Python給出的錯誤的時候,它也告訴了我們它檢測出錯誤的位置。所以我們從那行開始 調試 我們的程序。
通過仔細的觀察,我們發現一個邏輯行被分成了兩個物理行,但是我們并沒有指明這兩個物理行屬于同一邏輯行。基本上,Python發現加法操作符(+)在那一邏輯行沒有任何操作數,因此它不知道該如何繼續。記住我們可以使用物理行尾的反斜杠來表示邏輯行在下一物理行繼續。所以,我們修正了程序。這被稱為**修訂**。
```
#!/usr/bin/python
# Filename: backup_ver4.py
import os
import time
# 1\. The files and directories to be backed up are specified in a list.
source = ['/home/swaroop/byte', '/home/swaroop/bin']
# If you are using Windows, use source = [r'C:\Documents', r'D:\Work'] or something like that
# 2\. The backup must be stored in a main backup directory
target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using
# 3\. The files are backed up into a zip file.
# 4\. The current day is the name of the subdirectory in the main directory
today = target_dir + time.strftime('%Y%m%d')
# The current time is the name of the zip archive
now = time.strftime('%H%M%S')
# Take a comment from the user to create the name of the zip file
comment = raw_input('Enter a comment --> ')
if len(comment) == 0: # check if a comment was entered
????target = today + os.sep + now + '.zip'
else:
????target = today + os.sep + now + '_' + \
????????comment.replace(' ', '_') + '.zip'
????# Notice the backslash!
# Create the subdirectory if it isn't already there
if not os.path.exists(today):
????os.mkdir(today) # make directory
????print 'Successfully created directory', today
# 5\. We use the zip command (in Unix/Linux) to put the files in a zip archive
zip_command = "zip -qr '%s' %s" % (target, ' '.join(source))
# Run the backup
if os.system(zip_command) == 0:
????print 'Successful backup to', target
else:
????print 'Backup FAILED'
```
(源文件:[code/backup_ver4.py](code/backup_ver4.py))
## 輸出
```
$ python backup_ver4.py
Enter a comment --> added new examples
Successful backup to /mnt/e/backup/20041208/082156_added_new_examples.zip
$ python backup_ver4.py
Enter a comment -->
Successful backup to /mnt/e/backup/20041208/082316.zip
```
## 它如何工作
這個程序現在工作了!讓我們看一下版本三中作出的實質性改進。我們使用`raw_input`函數得到用戶的注釋,然后通過`len`函數找出輸入的長度以檢驗用戶是否確實輸入了什么東西。如果用戶只是按了**回車**(比如這只是一個慣例備份,沒有做什么特別的修改),那么我們就如之前那樣繼續操作。
然而,如果提供了注釋,那么它會被附加到zip歸檔名,就在`.zip`擴展名之前。注意我們把注釋中的空格替換成下劃線——這是因為處理這樣的文件名要容易得多。
對于大多數用戶來說,第四個版本是一個滿意的工作腳本了,但是它仍然有進一步改進的空間。比如,你可以在程序中包含 交互 程度——你可以用`-v`選項來使你的程序更具交互性。
另一個可能的改進是使文件和目錄能夠通過命令行直接傳遞給腳本。我們可以通過`sys.argv`列表來獲取它們,然后我們可以使用`list`類提供的`extend`方法把它們加到`source`列表中去。
我還希望有的一個優化是使用**tar**命令替代**zip**命令。這樣做的一個優勢是在你結合使用**tar**和**gzip**命令的時候,備份會更快更小。如果你想要在Windows中使用這些歸檔,WinZip也能方便地處理這些`.tar.gz`文件。**tar**命令在大多數Linux/Unix系統中都是默認可用的。Windows用戶也可以[下載](http://gnuwin32.sourceforge.net/packages/tar.htm)安裝它。
命令字符串現在將稱為:
```
tar = 'tar -cvzf %s %s -X /home/swaroop/excludes.txt' % (target, ' '.join(srcdir))
```
選項解釋如下:
* `-c`表示**創建**一個歸檔。
* `-v`表示**交互**,即命令更具交互性。
* `-z`表示使用**gzip**濾波器。
* `-f`表示**強迫**創建歸檔,即如果已經有一個同名文件,它會被替換。
* `-X`表示含在指定文件名列表中的文件會被**排除**在備份之外。例如,你可以在文件中指定`*~`,從而不讓備份包括所有以`~`結尾的文件。
重要
最理想的創建這些歸檔的方法是分別使用`zipfile`和`tarfile`。它們是Python標準庫的一部分,可以供你使用。使用這些庫就避免了使用`os.system`這個不推薦使用的函數,它容易引發嚴重的錯誤。
然而,我在本節中使用`os.system`的方法來創建備份,這純粹是為了教學的需要。這樣的話,例子就可以簡單到讓每個人都能夠理解,同時也已經足夠用了。
- 版權信息
- 前言
- 本書的由來
- 本書目前的狀況
- 約定條款
- 反饋
- 值得思考的一些東西
- 第1章 介紹
- Python的特色
- 為什么不使用Perl?
- 程序員的話
- 第2章 安裝Python
- Windows?用戶
- 概括
- 第3章 最初的步驟
- 使用帶提示符的解釋器
- 挑選一個編輯器
- 使用源文件
- 可執行的Python程序
- 獲取幫助
- 概括
- 第4章 基本概念
- 數
- 字符串
- 變量
- 標識符的命名
- 數據類型
- 對象
- 邏輯行與物理行
- 縮進
- 概括
- 第5章 運算符與表達式
- 運算符
- 運算符優先級
- 表達式
- 概括
- 第6章 控制流
- if語句
- while語句
- for循環
- break語句
- continue語句
- 概括
- 第7章 函數
- 函數形參
- 局部變量
- 默認參數值
- 關鍵參數
- return語句
- DocStrings
- 概括
- 第8章 模塊
- 字節編譯的.pyc文件
- from..import語句
- 模塊的name
- 制造你自己的模塊
- dir()函數
- 概括
- 第9章 數據結構
- 列表
- 元組
- 字典
- 序列
- 參考
- 更多字符串的內容
- 概括
- 第10章 解決問題——編寫一個Python腳本
- 解決方案
- 軟件開發過程
- 概括
- 第11章 面向對象的編程
- self
- 類
- 對象的方法
- __init__方法
- 類與對象的方法
- 繼承
- 概括
- 第12章 輸入/輸出
- 儲存器
- 概括
- 第13章 異常
- try..except
- 引發異常
- try..finally
- 概括
- 第14章 Python標準庫
- sys模塊
- os模塊
- 概括
- 第15章 更多Python的內容
- 單語句塊
- 列表綜合
- 在函數中接收元組和列表
- lambda形式
- exec和eval語句
- assert語句
- repr函數
- 概括
- 第16章 接下來學習什么?
- 探索更多內容
- 概括
- 附錄A 自由/開放源碼軟件(FLOSS)
- 附錄B 關于本書
- 關于作者
- 關于譯者
- 關于簡體中文譯本
- 附錄C 修訂記錄
- 術語表