os——可移植的訪問操作系統的特定功能

原文地址:https://pymotw.com/3/os/index.html

目的:可移植的訪問操作系統的特定功能。

os模塊為平臺特定模塊(比如posixntmac)提供了一個包裝器。所有平臺的函數API應該相同,所以使用os模塊提供了一些可移植的措施。但是,不是所有函數在每個平臺都有效。本文描述的很多進程管理函數在Windows上無效。

os模塊的Python文檔中,它的子標題是“各種操作系統接口”。該模塊主要包括創建和管理運行的進程或文件系統的內容(文件和目錄)的函數,以及一些其它功能。

1、檢查文件系統的內容

使用listdir()返回文件系統中一個目錄的內容列表。

# os_listdir.py

import os
import sys

print(os.listdir(sys.argv[1]))

返回值是給定目錄中所有命名成員的列表。文件,子目錄和符號鏈接之間沒有區別。

$ python3 os_listdir.py .

['index.rst', 'os_access.py', 'os_cwd_example.py',
'os_directories.py', 'os_environ_example.py',
'os_exec_example.py', 'os_fork_example.py',
'os_kill_example.py', 'os_listdir.py', 'os_listdir.py~',
'os_process_id_example.py', 'os_process_user_example.py',
'os_rename_replace.py', 'os_rename_replace.py~',
'os_scandir.py', 'os_scandir.py~', 'os_spawn_example.py',
'os_stat.py', 'os_stat_chmod.py', 'os_stat_chmod_example.txt',
'os_strerror.py', 'os_strerror.py~', 'os_symlinks.py',
'os_system_background.py', 'os_system_example.py',
'os_system_shell.py', 'os_wait_example.py',
'os_waitpid_example.py', 'os_walk.py']

walk()函數遞歸遍歷一個目錄,并為每個子目錄生成一個元組,其中包括目錄路徑,該路徑的所有直接子目錄,以及一個該目錄下所有文件名的列表。

# os_walk.py

import os
import sys

# If we are not given a path to list, use /tmp
if len(sys) == 1:
    root = '/tmp'
else:
    root = sys.argv[1]
    
for dir_name, sub_dirs, files in os.walk(root):
    print(dir_name)
    # Make the subdirectory names stand out with /
    sub_dirs = [n + '/' for n in sub_dirs]
    # Mix the directory contents together
    contents = sub_dirs + files
    contents.sort()
    # Show the contents
    for c in contents:
        print('  {}'.format(c))
    print()

這個例子顯示一個遞歸的目錄列表。

$ python3 os_walk.py ../zipimport

../zipimport
  __init__.py
  example_package/
  index.rst
  zipimport_example.zip
  zipimport_find_module.py
  zipimport_get_code.py
  zipimport_get_data.py
  zipimport_get_data_nozip.py
  zipimport_get_data_zip.py
  zipimport_get_source.py
  zipimport_is_package.py
  zipimport_load_module.py
  zipimport_make_example.py

../zipimport/example_package
  README.txt
  __init__.py

如果需要文件名之外的更多信息,scandir()listdir()更高效,因為掃描目錄時只需要執行一次系統調用就能收集更多信息。

# os_scandir.py

import os
import sys

for entry in os.scandir(sys.argv[1]):
    if entry.is_dir():
        typ = 'dir'
    elif entry.is_file():
        typ = 'file'
    elif entry.is_symlink():
        typ = 'link'
    else:
        typ = 'unknow'
    print('{name} {typ}'.format(
        name=entry.name,
        typ=typ,
    ))

scandir()返回為目錄中的項目返回一個DirEntry實例的序列。該對象有一些訪問文件元數據的屬性和方法。

$ python3 os_scandir.py .

index.rst file
os_access.py file
os_cwd_example.py file
os_directories.py file
os_environ_example.py file
os_exec_example.py file
os_fork_example.py file
os_kill_example.py file
os_listdir.py file
os_listdir.py~ file
os_process_id_example.py file
os_process_user_example.py file
os_rename_replace.py file
os_rename_replace.py~ file
os_scandir.py file
os_scandir.py~ file
os_spawn_example.py file
os_stat.py file
os_stat_chmod.py file
os_stat_chmod_example.txt file
os_strerror.py file
os_strerror.py~ file
os_symlinks.py file
os_system_background.py file
os_system_example.py file
os_system_shell.py file
os_wait_example.py file
os_waitpid_example.py file
os_walk.py file

2、管理文件系統的權限

使用stat()lstat()(用于檢查可能是符號鏈接的狀態)可以訪問一個文件的詳細信息。

# os_stat.py

import os
import sys
import time

if len(sys.argv) == 1:
    filename = __file__
else:
    filename = sys.argv[1]
    
stat_info = os.stat(filename)

print('os.stat({}):'.format(filename))
print('  Size:', stat_info.st_size)
print('  Permissions:', oct(stat_info.st_mode))
print('  Owner:', stat_info.st_uid)
print('  Device:', stat_info.st_dev)
print('  Created      :', time.ctime(stat_info.st_ctime))
print('  Last modified:', time.ctime(stat_info.st_mtime))
print('  Last accessed:', time.ctime(stat_info.st_atime))

輸出結果取決于示例代碼是如何運行的。嘗試在命令行中傳遞不同的文件名到os_stat.py中。

$ python3 os_stat.py

os.stat(os_stat.py):
  Size: 593
  Permissions: 0o100644
  Owner: 527
  Device: 16777218
  Created      : Sat Dec 17 12:09:51 2016
  Last modified: Sat Dec 17 12:09:51 2016
  Last accessed: Sat Dec 31 12:33:19 2016

$ python3 os_stat.py index.rst

os.stat(index.rst):
  Size: 26878
  Permissions: 0o100644
  Owner: 527
  Device: 16777218
  Created      : Sat Dec 31 12:33:10 2016
  Last modified: Sat Dec 31 12:33:10 2016
  Last accessed: Sat Dec 31 12:33:19 2016

在類Unix系統上,可以傳遞一個整數作為模式給chmod(),來修改文件權限。模式值可以使用在stat模塊中定義的常量構建。下面這個例子切換用戶的執行權限位:

# os_stat_chmod.py

import os
import stat

filename = 'os_stat_chmod_example.txt'
if os.path.exists(filename):
    os.unlink(filename)
with open(filename, 'wt') as f:
    f.write('content')
    
# Determine what permissions are already set using stat
existing_permissions = stat.S_IMODE(os.stat(filename).st_mode)

if not os.access(filename, os.X_OK):
    print('Adding excute permission')
    new_permissions = existing_permissions | stat.S_IXUSR
else:
    print('Removing execute permission')
    new_permissions = existing_permissions ^ stat.S_IXUSR
    
os.chmod(filename, new_permissions)

這個腳本假設它具有在運行時修改文件模式所需的權限。

$ python3 os_stat_chmod.py

Adding execute permission

access()函數用于測試進程對文件的訪問權限。

# os_access.py

print('Testing:', __file__)
print('Exists:', os.access(__file__, os.F_OK))
print('Readable:', os.access(__file__, os.R_OK))
print('Writable:', os.access(__file__, os.W_OK))
print('Executable:', os.access(__file__, os.X_OK))

結果取決于如何運行實例代碼,但輸出類似這樣:

$ python3 os_access.py

Testing: os_access.py
Exists: True
Readable: True
Writable: True
Executable: False

access()的庫文檔包括兩個特殊警告。第一個,在一個文件上調用open()之前,調用access()測試文件是否可以被打開沒有意義。在這兩次調用之間有很短的,但確實存在的時間間隔,期間文件的權限有可能被改變。另一個警告主要適用于擴展自POSIX權限語義的聯網文件系統。某些文件系統類型可能會響應進程有權限訪問文件的POSIX調用,然后由于某些原因沒有通過POSIX調用測試,嘗試使用open()時會出錯。總而言之,最好使用需要的模式調用open(),并捕獲出錯時拋出的IOError

3、創建和刪除目錄

有幾個函數用于操作文件系統中的目錄,包括創建,列出內容和刪除它們。

# os_directories.py

dir_name = 'os_directories_example'

print('Creating', dir_name)
os.makedirs(dir_name)

file_name = os.path.join(dir_name, 'example.txt')
print('Creating', file_name)
with open(file_name, 'wt') as f:
    f.write('example file')

print('Cleaning up')
os.unlink(file_name)
os.rmdir(dir_name)

有兩個函數集用于創建和刪除目錄。使用mkdir()創建一個新目錄時,所有父目錄必須已經存在。使用rmdir()刪除一個目錄時,只有葉子目錄(路徑的最后一部分)會真正的被刪除。相反,makedirs()removedirs()會操作路徑中的所有節點。makedirs()會創建路徑中所有不存在的部分,removedirs()會刪除所有父目錄,知道它們為空。

$ python3 os_directories.py

Creating os_directories_example
Creating os_directories_example/example.txt
Cleaning up

4、使用符號鏈接

對于支持符號鏈接的平臺和文件系統,有些函數用于符號鏈接。

# os_symlinks.py

import os

link_name = '/tmp/' + os.path.basename(__file__)

print('Creating link {} -> {}'.format(link_name, __file__))
os.symlink(__file__, link_name)

stat_info = os.lstat(link_name)
print('Permissions:', oct(stat_info.st_mode))

print('Points to:', os.readlink(link_name))

# Cleanup
os.unlink(link_name)

使用symlink()創建一個符號鏈接,readlink()讀取鏈接,確定它指向的原始文件。lstat()函數類似stat(),但是它在符號鏈接上運行。

$ python3 os_symlinks.py

Creating link /tmp/os_symlinks.py -> os_symlinks.py
Permissions: 0o120755
Points to: os_symlinks.py

5、安全地替換已存在的文件

替換或重命名已存在文件不是冪等的,并且可能把應用程序暴露于競爭條件。對于這些操作,rename()replace()函數可以在兼容POSIX系統上使用原子操作來實現安全的算法。

# os_rename_replace.py

import glob
import os

with open('rename_start.txt', 'w') as f:
    f.write('starting as rename_start.txt')
    
print('Starting:', glob.glob('rename*.txt'))

os.rename('rename_start.txt', 'rename_finish.txt')

print('After rename:', glob.glob('rename*.txt'))

with open('rename_finish.txt', 'r') as f:
    print('Contents:', repr(f.read()))
    
with open('rename_new_contents.txt', 'w') as f:
    f.write(ending with contents of rename_new_contents.txt')
    
os.replace('rename_new_contents.txt', 'rename_finish.txt')

with open('rename_new_contents.txt', 'r') as f:
    print('After replace:', repr(f.read()))
    
for name in glob.glob('rename*.txt'):
    os.unlink(name)

大多數時候,rename()replace()函數可以跨文件系統工作。如果將文件移動到新文件系統,或者目標已經存在,則重命名文件可能會失敗。

$ python3 os_rename_replace.py

Starting: ['rename_start.txt']
After rename: ['rename_finish.txt']
Contents: 'starting as rename_start.txt'
After replace: 'ending with contents of rename_new_contents.txt'

6、檢測和修改進程所有者

os提供的下一組函數用于確定和修改進程所有者的ID。這最常被守護程序或特殊系統程序的作者用于修改權限級別,而不是以root身份運行。本節不會解釋Unix安全性,進程所有者等的所有錯綜復雜的細節。有關詳細信息請參考本節末尾的參考列表。

下面這個示例顯示了一個進程的real和effective用戶和組的信息,然后修改effective值。這類似于系統引導期間,守護程序以root身份啟動時需要完成一些工作,用于降低權限級別,并以另一個用戶運行。

注意:運行示例之前,修改TEST_GIDTEST_UID值為系統中定義的real用戶。

# os_process_user_example.py

import os

TEST_GID = 502
TEST_UID = 502

def show_user_info():
    print('User (actual/effective)  :  {} / {}'.format(
        os.getuid(), os.geteuid()))
    print('Group (actual/effective)  :  {} / {}'.format(
        os.getgid(), os.getegid()))
    print('Actual Groups  :', os.getgroups())
    
print('BEFORE CHANGE:')
show_user_info()
print()

try:
    os.setgid(TEST_ID)
except OSError:
    print('ERROR: Could not change effective group. '
          'Rerun as root.')
else:
    print('CHANGE GROUP:')
    show_user_info()
    print()

try:
    os.seteuid(TEST_ID)
except OSError:
    print('ERROR: Could not change effective user. '
          'Rerun as root.')
else:
    print('CHANGE USER:')
    show_user_info()
    print()

當在OS X上運行的用戶ID為502,組為502時,產生以下輸出:

$ python3 os_process_user_example.py

BEFORE CHANGE:
User (actual/effective)  : 527 / 527
Group (actual/effective) : 501 / 501
Actual Groups   : [501, 701, 402, 702, 500, 12, 61, 80, 98, 398,
399, 33, 100, 204, 395]

ERROR: Could not change effective group. Rerun as root.
ERROR: Could not change effective user. Rerun as root.

值不會修改,因為它沒有作為root運行,一個進程不能修改它的effective所有者的值。任何設置effective user id或group id為當前用戶之外的其它值都會導致OSError。使用sudo運行同一個腳本,用root權限開始一個不同的場景。

$ sudo python3 os_process_user_example.py

BEFORE CHANGE:

User (actual/effective)  : 0 / 0
Group (actual/effective) : 0 / 0
Actual Groups : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]

CHANGE GROUP:
User (actual/effective)  : 0 / 0
Group (actual/effective) : 0 / 502
Actual Groups   : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]

CHANGE USER:
User (actual/effective)  : 0 / 502
Group (actual/effective) : 0 / 502
Actual Groups   : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]

這種情況下,因為用root啟動,所以腳本會修改進程的effective user和group。一旦修改了effective UID,進程會被限制在該用戶的權限下。因為非root用戶不能修改它們的effective group,所以程序需要在修改用戶之前修改組。

7、管理進程環境

操作系統通過os模塊暴露給程序的另一個特征是環境。在環境中設置的變量作為字符串可見,可以通過os.environgetenv()讀取。環境變量通常用于配置搜索路徑,文件位置和調試標識等值。這個示例顯示了如何檢索一個環境變量,以及傳遞一個值給子進程。

# os_environ_example.py

import os

print('Initial value:', os.environ.get('TESTVAR', None))
print('Child process:')
os.system('echo $TESTVAR')

os.environ['TESTVAR'] = 'THIS VALUE WAS CHANGED'

print()
print('Changed value:', os.environ['TESTVAR'])
print('Child process:')
os.system('echo $TESTVAR')

del os.environ['TESTVAR']

print()
print('Remove value:', os.environ.get('TESTVAR', None))
print('Child process:')
os.system('echo $TESTVAR')

os.environ對象遵循標準的Python映射API,用于檢索和設置值。對os.environ的修改會為子進程導出。

$ python3 -u os_environ_example.py

Initial value: None
Child process:


Changed value: THIS VALUE WAS CHANGED
Child process:
THIS VALUE WAS CHANGED

Removed value: None
Child process:

8、管理進程工作目錄

帶層級文件系統的操作系統有當前工作目錄的概念——當用相對路徑訪問文件時,進程用作起始位置的目錄。可以使用getcwd()查詢當前工作目錄,使用chdir()修改。

# os_cwd_example.py

import os

print('Starting:', os.getcwd())

print('Moving up one:', os.pardir)
os.chdir(os.pardir)

print('After move:', os.getcwd())

os.curdiros.pardir用于指向當前目錄和父目錄。

$ python3 os_cwd_example.py

Starting: .../pymotw-3/source/os
Moving up one: ..
After move: .../pymotw-3/source

9、運行外部命令

警告:
用于處理進程的很多函數的移植性是有限制的。要以平臺獨立的方式,以更一致的方法使用進程,請參考subprocess模塊。

運行獨立命令的最基本的,并且不與它交互的方式是使用system()。它接收單個字符串參數,它是被子進程在shell中執行的命令行。

# os_system_example.py

import os

# Simple command
os.system('pwd')

system()的返回值是shell執行程序的退出值,它被打包成一個16 bit的數字,其中高字節是退出狀態,低字節是導致進程死亡的信號數或者0。

$ python3 -u os_system_example.py

.../pymotw-3/source/os

因為命令直接傳遞給shell處理,所以可以包括shell語法,比如通配符或者環境變量。

# os_system_shell.py

import os

# Command with shell expansion
os.system('echo $TMPDIR')

當shell執行命令行時,字符串中的環境變量TMPDIR會被展開。

$ python3 -u os_system_shell.py

/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/

除非命令明確地在后臺執行,否則調用system()會阻塞,直到完成執行。默認情況下,子進程的標準輸入,輸出和錯誤與調用者擁有的適當流關聯,但是可以使用shell語法重定向。

# os_system_background.py

import os
import time

print('Calling...')
os.system('date; (sleep 3; date) &')

print('Sleeping...')
time.sleep(5)

盡管這正好掉進了shell的詭計中,但是有更好的方式完成同樣的任務。

$ python3 -u os_system_background.py

Calling...
Sat Dec 31 12:33:20 EST 2016
Sleeping...
Sat Dec 31 12:33:23 EST 2016

10、使用os.fork()創建進程

POSIX函數fork()exec()(在Mac OS X,Linux和其它Unix版本下可用)通過os模塊暴露。已經有很多書籍介紹了可靠的使用這些函數,所以在圖書館或書店查閱更多細節,這里只是一個簡介。

使用fork()創建一個新進程,作為當前進程的克隆:

# os_fork_example.py

import os

pid = os.fork()

if pid:
    print('Child process id:', pid)
else:
    print('I am the child')

每次運行示例時,輸入取決于系統的狀態,但它看起來是這樣的:

$ python3 -u os_fork_example.py

Child process id: 29190
I am the child

fork之后會有兩個進程執行同樣的代碼。通過檢查fork()的返回值,告訴程序在哪個進程中。如果值為0,則當前是子進程。如果不為0,則程序在父進程中執行,返回值是子進程的進程ID。

# os_kill_example.py

import os
import signal
import time


def signal_usr1(signum, frame):
    "Callback invoked when a signal is received"
    pid = os.getpid()
    print('Received USR1 in process {}'.format(pid))


print('Forking...')
child_pid = os.fork()
if child_pid:
    print('PARENT: Pausing before sending signal...')
    time.sleep(1)
    print('PARENT: Signaling {}'.format(child_pid))
    os.kill(child_pid, signal.SIGUSR1)
else:
    print('CHILD: Setting up signal handler')
    signal.signal(signal.SIGUSR1, signal_usr1)
    print('CHILD: Pausing to wait for signal')
    time.sleep(5)

父進程可以使用kill()signal模塊發送信號到子進程。首先定義一個接收到信號時觸發的信號處理器。然后fork(),并在用kill()發送USR1信號之前,在父進程中暫停一小段時間。這個示例用暫停讓子進程有時間設置信號處理器。實際程序中,不需要(或者不希望)調用sleep()。在子進程中設置信號處理器,并休眠一段時間,讓父進程發送信號。

$ python3 -u os_kill_example.py

Forking...
PARENT: Pausing before sending signal...
CHILD: Setting up signal handler
CHILD: Pausing to wait for signal
PARENT: Signaling 29193
Received USR1 in process 29193

在子進程中處理獨立行為的一種簡單方法是檢查fork()的返回值,然后分支。更復雜的行為可能需要比簡單分支更多的獨立代碼。其它情況下,可能需要包裝已經存在的程序。對于這兩種情況,exec*()函數系列可以用于運行其它程序。

# os_exec_example.py

import os

child_pid = os.fork()
if child_pid:
    os.waitpid(child_pid, 0)
else:
    os.execlp('pwd', 'pwd', '-P')

當程序通過exec()運行時,該程序的代碼會替換已存在進程中的代碼。

$ python3 os_exec_example.py

.../pymotw-3/source/os

根據參數的可用形式,父進程的路徑和環境是否應該拷貝到子進程中等,exec()有很多變體。對于所有變種,第一個參數是路徑或文件名,剩余的參數控制程序如何執行。它們作為命令行參數傳遞,或者覆蓋進程環境(參考os.environos.getenv)。完整細節請參考庫文檔。

11、等待子進程

很多計算密集型程序使用多進程來解決Python和全局解釋器鎖的線程限制。當啟動多個進程執行獨立的任務時,主機需要等待其中一個或多個進程完成,然后再啟動新進程,以避免服務器超負荷。使用wait()和相關函數有幾種不同的方式。

不管哪個子進程先退出不重要時,使用wait()。一旦任何子進程退出就會返回。

# os_wait_example.py

import os
import sys
import time

for i in range(2):
    print('PARENT {}: FORKING {}'.format(os.getpid(), i))
    worker_pid = os.fork()
    if not worker_pid:
        print('WORKER {}: Starting'.format(i))
        time.sleep(2 + i)
        print('WORKER {}: Finishing'.format(i))
        sys.exit(i)

for i in range(2):
    print('PARENT: Waiting for {}'.format(i))
    done = os.wait()
    print('PARENT: Child done:', done)

wait()的返回值是一個元組,其中包括進程ID和組合為16 bit值的退出狀態。低字節是殺死進程的信號數字,高字節是進程退出時返回的狀態碼。

$ python3 -u os_wait_example.py

PARENT 29202: Forking 0
PARENT 29202: Forking 1
PARENT: Waiting for 0
WORKER 0: Starting
WORKER 1: Starting
WORKER 0: Finishing
PARENT: Child done: (29203, 0)
PARENT: Waiting for 1
WORKER 1: Finishing
PARENT: Child done: (29204, 256)

使用waitpid()等待指定進程。

# os_waitpid_example.py

import os
import sys
import time

workers = []
for i in range(2):
    print('PARENT {}: Forking {}'.format(os.getpid(), i))
    worker_pid = os.fork()
    if not worker_pid:
        print('WORKER {}: Starting'.format(i))
        time.sleep(2 + i)
        print('WORKER {}: Finishing'.format(i))
        sys.exit(i)
    workers.append(worker_pid)

for pid in workers:
    print('PARENT: Waiting for {}'.format(pid))
    done = os.waitpid(pid, 0)
    print('PARENT: Child done:', done)

傳遞目標進程的進程ID,waitpid()會阻塞直到該進程退出。

$ python3 -u os_waitpid_example.py

PARENT 29211: Forking 0
PARENT 29211: Forking 1
PARENT: Waiting for 29212
WORKER 0: Starting
WORKER 1: Starting
WORKER 0: Finishing
PARENT: Child done: (29212, 0)
PARENT: Waiting for 29213
WORKER 1: Finishing
PARENT: Child done: (29213, 256)

wait3()wait4()以類似的方式工作,但是會返回子進程更詳細的信息,包括pid,退出狀態和資源使用情況。

12、生成新進程

為了方便,spawn()函數族在一條語句中處理fork()exec()

# os_spawn_example.py

import os

os.spawnlp(os.P_WAIT, 'pwd', 'pwd', '-P')

第一個參數是模式,指定返回之前是否等待進程完成。這個示例中等待進程完成。使用P_NOWAIT讓其它進程啟動,然后在當前進程中繼續。

$ python3 os_spawn_example.py

.../pymotw-3/source/os

13、操作系統錯誤碼

由操作系統定義,并在errno模塊中管理的錯誤碼可以使用strerror()翻譯為消息字符串。

# os_strerror.py

import errno
import os

for num in [errno.ENOENT, errno.EINTR, errno.EBUSY]:
    name = errno.errorcode[num]
    print('[{num:>2}] {name:<6}: {msg}'.format(
        name=name, num=num, msg=os.strerror(num)))

這個示例顯示了與某些頻繁出現的錯誤代碼相關聯的消息。

$ python3 os_strerror.py

[ 2] ENOENT: No such file or directory
[ 4] EINTR : Interrupted system call
[16] EBUSY : Resource busy

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容