python標准庫介紹——1 os詳解


== os 模塊 ==

``os`` 模塊為許多操作系統函數提供了統一的接口.

這個模塊中的大部分函數通過對應平台相關模塊實現, 比如 ``posix`` 和 
``nt. os`` 模塊會在第一次導入的時候自動加載合適的執行模塊. 

=== 處理文件===

內建的 ``open / file`` 函數用於創建, 打開和編輯文件, 如 [Example 1-27 #eg-1-27] 所示. 而 
``os`` 模塊提供了重命名和刪除文件所需的函數. 

====Example 1-27. 使用 os 模塊重命名和刪除文件====[eg-1-27]

```
File: os-example-3.py

import os
import string

def replace(file, search_for, replace_with):
    # replace strings in a text file

    back = os.path.splitext(file)[0] + ".bak"
    temp = os.path.splitext(file)[0] + ".tmp"

    try:
        # remove old temp file, if any
        os.remove(temp)
    except os.error:
        pass

    fi = open(file)
    fo = open(temp, "w")

    for s in fi.readlines():
        fo.write(string.replace(s, search_for, replace_with))

    fi.close()
    fo.close()

    try:
        # remove old backup file, if any
        os.remove(back)
    except os.error:
        pass

    # rename original to backup...
    os.rename(file, back)

    # ...and temporary to original
    os.rename(temp, file)

#
# try it out!

file = "samples/sample.txt"

replace(file, "hello", "tjena")
replace(file, "tjena", "hello")
```

=== 處理目錄===

``os`` 模塊也包含了一些用於目錄處理的函數. 

``listdir`` 函數返回給定目錄中所有文件名(包括目錄名)組成的列表, 如 
[Example 1-28 #eg-1-28] 所示. 而 Unix 和 Windows 中使用的當前目錄和父目錄標記(. 和 .. )不包含在此列表中. 

====Example 1-28. 使用 os 列出目錄下的文件====[eg-1-28]

```
File: os-example-5.py

import os

for file in os.listdir("samples"):
    print file

*B*sample.au
sample.jpg
sample.wav
...*b*
```

``getcwd`` 和 ``chdir`` 函數分別用於獲得和改變當前工作目錄. 如 [Example 1-29 #eg-1-29] 所示. 

====Example 1-29. 使用 os 模塊改變當前工作目錄====[eg-1-29]

```
File: os-example-4.py

import os

# where are we?
cwd = os.getcwd()
print "1", cwd

# go down
os.chdir("samples")
print "2", os.getcwd()

# go back up
os.chdir(os.pardir)
print "3", os.getcwd()

*B*1 /ematter/librarybook
2 /ematter/librarybook/samples
3 /ematter/librarybook*b*
```

``makedirs`` 和 ``removedirs`` 函數用於創建或刪除目錄層,如 [Example 1-30 #eg-1-30] 所示. 

====Example 1-30. 使用 os 模塊創建/刪除多個目錄級====[eg-1-30]

```
File: os-example-6.py

import os

os.makedirs("test/multiple/levels")

fp = open("test/multiple/levels/file", "w")
fp.write("inspector praline")
fp.close()

# remove the file
os.remove("test/multiple/levels/file")

# and all empty directories above it
os.removedirs("test/multiple/levels")
```

``removedirs`` 函數會刪除所給路徑中最后一個目錄下所有的空目錄. 
而 ``mkdir`` 和 ``rmdir`` 函數只能處理單個目錄級. 如 [Example 1-31 #eg-1-31] 所示.

====Example 1-31. 使用 os 模塊創建/刪除目錄====[eg-1-31]

```
File: os-example-7.py

import os

os.mkdir("test")
os.rmdir("test")

os.rmdir("samples") # this will fail

*B*Traceback (innermost last):
  File "os-example-7", line 6, in ?
OSError: [Errno 41] Directory not empty: 'samples'*b*
```

如果需要刪除非空目錄, 你可以使用 ``shutil`` 模塊中的 ``rmtree`` 函數. 

=== 處理文件屬性===
``stat`` 函數可以用來獲取一個存在文件的信息, 如 [Example 1-32 #eg-1-32] 
所示. 它返回一個類元組對象(stat_result對象, 包含 10 個元素), 
依次是st_mode (權限模式), st_ino (inode number), st_dev (device), 
st_nlink (number of hard links), st_uid (所有者用戶 ID), st_gid 
(所有者所在組 ID ), st_size (文件大小, 字節), st_atime (最近一次訪問時間), 
st_mtime (最近修改時間), st_ctime (平台相關; Unix下的最近一次元數據/metadata修改時間, 
或者 Windows 下的創建時間) - 以上項目也可作為屬性訪問. 

``` [!Feather 注: 原文為 9 元元組. 另,返回對象並非元組類型,為 struct.]

====Example 1-32. 使用 os 模塊獲取文件屬性====[eg-1-32]

```
File: os-example-1.py

import os
import time

file = "samples/sample.jpg"

def dump(st):
    mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime = st
    print "- size:", size, "bytes"
    print "- owner:", uid, gid
    print "- created:", time.ctime(ctime)
    print "- last accessed:", time.ctime(atime)
    print "- last modified:", time.ctime(mtime)
    print "- mode:", oct(mode)
    print "- inode/dev:", ino, dev

#
# get stats for a filename

st = os.stat(file)

print "stat", file
dump(st)
print

#
# get stats for an open file

fp = open(file)

st = os.fstat(fp.fileno())

print "fstat", file
dump(st)

*B*stat samples/sample.jpg
- size: 4762 bytes
- owner: 0 0
- created: Tue Sep 07 22:45:58 1999
- last accessed: Sun Sep 19 00:00:00 1999
- last modified: Sun May 19 01:42:16 1996
- mode: 0100666
- inode/dev: 0 2

fstat samples/sample.jpg
- size: 4762 bytes
- owner: 0 0
- created: Tue Sep 07 22:45:58 1999
- last accessed: Sun Sep 19 00:00:00 1999
- last modified: Sun May 19 01:42:16 1996
- mode: 0100666
- inode/dev: 0 0*b*
```

返回對象中有些屬性在非 Unix 平台下是無意義的, 比如 (``st_inode`` , ``st_dev``)為 
Unix 下的為每個文件提供了唯一標識, 但在其他平台可能為任意無意義數據 .

``stat`` 模塊包含了很多可以處理該返回對象的常量及函數. 下面的代碼展示了其中的一些. 

可以使用 ``chmod`` 和 ``utime`` 函數修改文件的權限模式和時間屬性,如 [Example 1-33 #eg-1-33] 所示. 

====Example 1-33. 使用 os 模塊修改文件的權限和時間戳====[eg-1-33]

```
File: os-example-2.py

import os
import stat, time

infile = "samples/sample.jpg"
outfile = "out.jpg"

# copy contents
fi = open(infile, "rb")
fo = open(outfile, "wb")

while 1:
    s = fi.read(10000)
    if not s:
        break
    fo.write(s)

fi.close()
fo.close()

# copy mode and timestamp
st = os.stat(infile)
os.chmod(outfile, stat.S_IMODE(st[stat.ST_MODE]))
os.utime(outfile, (st[stat.ST_ATIME], st[stat.ST_MTIME]))

print "original", "=>"
print "mode", oct(stat.S_IMODE(st[stat.ST_MODE]))
print "atime", time.ctime(st[stat.ST_ATIME])
print "mtime", time.ctime(st[stat.ST_MTIME])

print "copy", "=>"
st = os.stat(outfile)
print "mode", oct(stat.S_IMODE(st[stat.ST_MODE]))
print "atime", time.ctime(st[stat.ST_ATIME])
print "mtime", time.ctime(st[stat.ST_MTIME])

*B*original =>
mode 0666
atime Thu Oct 14 15:15:50 1999
mtime Mon Nov 13 15:42:36 1995
copy =>
mode 0666
atime Thu Oct 14 15:15:50 1999
mtime Mon Nov 13 15:42:36 1995*b*
```

=== 處理進程===

``system`` 函數在當前進程下執行一個新命令, 並等待它完成, 如 [Example 1-34 #eg-1-34] 所示. 

====Example 1-34. 使用 os 執行操作系統命令====[eg-1-34]

```
File: os-example-8.py

import os

if os.name == "nt":
    command = "dir"
else:
    command = "ls -l"

os.system(command)

*B*-rwxrw-r--   1 effbot  effbot        76 Oct  9 14:17 README
-rwxrw-r--   1 effbot  effbot      1727 Oct  7 19:00 SimpleAsyncHTTP.py
-rwxrw-r--   1 effbot  effbot       314 Oct  7 20:29 aifc-example-1.py
-rwxrw-r--   1 effbot  effbot       259 Oct  7 20:38 anydbm-example-1.py
...*b*
```

命令通過操作系統的標准 shell 執行, 並返回 shell 的退出狀態. 需要注意的是在 Windows 95/98 
下, shell 通常是 ``command.com`` , 它的推出狀態總是 0. 

        由於 11os.system11 直接將命令傳遞給 shell , 所以如果你不檢查傳入參數的時候會很危險 
        (比如命令 ``os.system("viewer %s" % file)``, 將 file 變量設置為 
        "``sample.jpg; rm -rf $HOME" ....``). 如果不確定參數的安全性, 那么最好使用
        ``exec`` 或 ``spawn`` 代替(稍后介紹).

 
``exec`` 函數會使用新進程替換當前進程(或者說是"轉到進程"). 在 [Example 1-35 #eg-1-35] 中, 
字符串 "goodbye" 永遠不會被打印. 

====Example 1-35. 使用 os 模塊啟動新進程====[eg-1-35]

```
File: os-exec-example-1.py

import os
import sys

program = "python"
arguments = ["hello.py"]

print os.execvp(program, (program,) +  tuple(arguments))
print "goodbye"

*B*hello again, and welcome to the show*b*
```

Python 提供了很多表現不同的 ``exec`` 函數. [Example 1-35 #eg-1-35] 使用的是 
``execvp`` 函數, 它會從標准路徑搜索執行程序, 把第二個參數(元組)作為單獨的參數傳遞給程序, 並使用當前的環境變量來運行程序. 其他七個同類型函數請參閱 //Python Library Reference// .

在 Unix 環境下, 你可以通過組合使用 ``exec`` , ``fork`` 以及 ``wait`` 函數來從當前程序調用另一個程序, 
如 [Example 1-36 #eg-1-36] 所示. ``fork`` 函數復制當前進程, ``wait`` 函數會等待一個子進程執行結束. 

====Example 1-36. 使用 os 模塊調用其他程序 (Unix)====[eg-1-36]

```
File: os-exec-example-2.py

import os
import sys

def run(program, *args):
    pid = os.fork()
    if not pid:
        os.execvp(program, (program,) +  args)
    return os.wait()[0]

run("python", "hello.py")

print "goodbye"

*B*hello again, and welcome to the show
goodbye*b*
```

``fork`` 函數在子進程返回中返回 0 (這個進程首先從 ``fork`` 返回值), 
在父進程中返回一個非 0 的進程標識符(子進程的 PID ). 也就是說, 
只有當我們處於子進程的時候 "``not pid``" 才為真. 

``fork`` 和 ``wait`` 函數在 Windows 上是不可用的, 但是你可以使用 ``spawn`` 函數, 
如 [Example 1-37 #eg-1-37] 所示. 不過, ``spawn`` 不會沿着路徑搜索可執行文件, 
你必須自己處理好這些. 

====Example 1-37. 使用 os 模塊調用其他程序 (Windows)====[eg-1-37]

```
File: os-spawn-example-1.py

import os
import string

def run(program, *args):
    # find executable
    for path in string.split(os.environ["PATH"], os.pathsep):
        file = os.path.join(path, program) + ".exe"
        try:
            return os.spawnv(os.P_WAIT, file, (file,) + args)
        except os.error:
            pass
    raise os.error, "cannot find executable"

run("python", "hello.py")

print "goodbye"

*B*hello again, and welcome to the show
goodbye*b*
```

``spawn`` 函數還可用於在后台運行一個程序. [Example 1-38 #eg-1-38] 給 ``run`` 函數添加了一個可選的 
``mode`` 參數; 當設置為 ``os.P_NOWAIT`` 時, 這個腳本不會等待子程序結束, 
默認值 ``os.P_WAIT`` 時 ``spawn`` 會等待子進程結束. 

其它的標志常量還有 ``os.P_OVERLAY`` ,它使得 ``spawn`` 的行為和 ``exec`` 類似, 
以及 ``os.P_DETACH`` , 它在后台運行子進程, 與當前控制台和鍵盤焦點隔離. 

====Example 1-38. 使用 os 模塊在后台執行程序 (Windows)====[eg-1-38]

```
File: os-spawn-example-2.py

import os
import string

def run(program, *args, **kw):
    # find executable
    mode = kw.get("mode", os.P_WAIT)
    for path in string.split(os.environ["PATH"], os.pathsep):
        file = os.path.join(path, program) + ".exe"
        try:
            return os.spawnv(mode, file, (file,) + args)
        except os.error:
            pass
    raise os.error, "cannot find executable"

run("python", "hello.py", mode=os.P_NOWAIT)
print "goodbye"

*B*goodbye
hello again, and welcome to the show*b*
```

[Example 1-39 #eg-1-39] 提供了一個在 Unix 和 Windows 平台上通用的 ``spawn`` 方法. 

====Example 1-39. 使用 spawn 或 fork/exec 調用其他程序====[eg-1-39]

```
File: os-spawn-example-3.py

import os
import string

if os.name in ("nt", "dos"):
    exefile = ".exe"
else:
    exefile = ""

def spawn(program, *args):
    try:
        # possible 2.0 shortcut!
        return os.spawnvp(program, (program,) + args)
    except AttributeError:
        pass
    try:
        spawnv = os.spawnv
    except AttributeError:

        # assume it's unix
        pid = os.fork()
        if not pid:
            os.execvp(program, (program,) + args)
        return os.wait()[0]
    else:
        # got spawnv but no spawnp: go look for an executable
        for path in string.split(os.environ["PATH"], os.pathsep):
            file = os.path.join(path, program) + exefile
            try:
                return spawnv(os.P_WAIT, file, (file,) + args)
            except os.error:
                pass
        raise IOError, "cannot find executable"

#
# try it out!

spawn("python", "hello.py")

print "goodbye"

*B*hello again, and welcome to the show
goodbye*b*
```

[Example 1-39 #eg-1-39] 首先嘗試調用 ``spawnvp`` 函數. 如果該函數不存在
(一些版本/平台沒有這個函數), 它將繼續查找一個名為 ``spawnv`` 的函數並且
開始查找程序路徑. 作為最后的選擇, 它會調用 ``exec`` 和 ``fork`` 函數完成工作. 

=== 處理守護進程(Daemon Processes)===

Unix 系統中, 你可以使用 ``fork`` 函數把當前進程轉入后台(一個"守護者/daemon"). 一般來說, 你需要派生(fork off)一個當前進程的副本, 然后終止原進程, 如 [Example 1-40 #eg-1-40] 所示. 

====Example 1-40. 使用 os 模塊使腳本作為守護執行 (Unix)====[eg-1-40]

```
File: os-example-14.py

import os
import time

pid = os.fork()
if pid:
    os._exit(0) # kill original

print "daemon started"
time.sleep(10)
print "daemon terminated"
```

需要創建一個真正的后台程序稍微有點復雜, 首先調用 ``setpgrp`` 函數創建一個 "進程組首領/process group leader". 否則, 向無關進程組發送的信號(同時)會引起守護進程的問題: 

``` os.setpgrp()

為了確保守護進程創建的文件能夠獲得程序指定的 mode flags(權限模式標記?), 最好刪除 user mode mask:

``` os.umask(0)

然后, 你應該重定向 //stdout/stderr// 文件, 而不能只是簡單地關閉它們(如果你的程序需要 ``stdout`` 
或 ``stderr`` 寫入內容的時候, 可能會出現意想不到的問題). 

```
class NullDevice:
    def write(self, s):
        pass
sys.stdin.close()
sys.stdout = NullDevice()
sys.stderr = NullDevice()
```

換言之, 由於 Python 的 ``print`` 和 C 中的 ``printf/fprintf`` 在設備(device)
沒有連接后不會關閉你的程序, 此時守護進程中的 ``sys.stdout.write()`` 會拋出一個 //IOError// 異常, 而你的程序依然在后台運行的很好.... 

另外, 先前例子中的 ``_exit`` 函數會終止當前進程. 而 ``sys.exit`` 不同, 如果調用者(caller)
捕獲了 //SystemExit// 異常, 程序仍然會繼續執行. 如 [Example 1-41 #eg-1-41] 所示.

====Example 1-41. 使用 os 模塊終止當前進程====[eg-1-41]

```
File: os-example-9.py

import os
import sys

try:
    sys.exit(1)
except SystemExit, value:
    print "caught exit(%s)" % value

try:
    os._exit(2)
except SystemExit, value:
    print "caught exit(%s)" % value

print "bye!"

*B*caught exit(1)*b*
```

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM