Shell腳本:模塊引用饺汹,提高代碼復(fù)用性和可維護性題

Shell腳本:模塊引用袍冷,提高代碼復(fù)用性和可維護性

目錄

1. 引言

2. Shell腳本模塊化的重要性

3. 基本的模塊引用方法

? 3.1 使用source命令

? 3.2 使用點號(.)操作符

4. 創(chuàng)建和組織模塊

? 4.1 函數(shù)模塊

? 4.2 變量模塊

? 4.3 常量模塊

5. 高級模塊引用技巧

? 5.1 相對路徑和絕對路徑

? 5.2 動態(tài)模塊加載

? 5.3 條件模塊加載

6. 模塊化最佳實踐

? 6.1 命名約定

? 6.2 文檔和注釋

? 6.3 版本控制

7. 常見問題和解決方案

? 7.1 循環(huán)依賴

? 7.2 命名沖突

? 7.3 性能考慮

8. 實戰(zhàn)項目:構(gòu)建模塊化的Shell應(yīng)用

9. 總結(jié)

1. 引言

在Shell腳本編程中做个,隨著項目規(guī)模的增長,代碼的組織和管理變得越來越重要聊闯。模塊化編程是一種強大的技術(shù),它允許我們將大型米诉、復(fù)雜的腳本拆分成更小菱蔬、更易于管理的部分匀伏。本文將深入探討Shell腳本中的模塊引用技術(shù)宠页,幫助您編寫更清晰、更高效的代碼肛炮。

2. Shell腳本模塊化的重要性

模塊化編程在Shell腳本開發(fā)中具有多重重要性:

  • 代碼復(fù)用:通過將常用功能封裝到模塊中惊橱,我們可以在多個腳本中重復(fù)使用這些功能蚪腐,而無需復(fù)制粘貼代碼。
  • 可維護性:將大型腳本分解成小型税朴、獨立的模塊回季,使得代碼更容易理解和維護。
  • 協(xié)作開發(fā):模塊化使得團隊成員可以并行工作在不同的模塊上正林,提高開發(fā)效率泡一。
  • 測試性:獨立的模塊更容易進行單元測試,提高代碼質(zhì)量卓囚。
  • 靈活性:模塊化設(shè)計允許更容易地替換或升級特定功能瘾杭,而不影響整個系統(tǒng)。
  • 接下來哪亿,我們將通過一系列實例來探索如何在Shell腳本中實現(xiàn)和利用模塊化粥烁。

    3. 基本的模塊引用方法

    在Shell腳本中,有兩種主要的方法來引用外部模塊:使用source命令和使用點號(.)操作符蝇棉。這兩種方法本質(zhì)上是等價的讨阻,選擇哪一種主要取決于個人偏好和可讀性考慮。

    3.1 使用source命令

    source命令是引用外部Shell腳本的常用方法篡殷。它會在當(dāng)前Shell環(huán)境中執(zhí)行指定的腳本钝吮,使得被引用腳本中定義的所有變量和函數(shù)在當(dāng)前腳本中可用。

    示例1:基本的source使用

    假設(shè)我們有一個名為math_functions.sh的模塊板辽,其中定義了一些數(shù)學(xué)函數(shù):

    # math_functions.sh#!/bin/bashfunction add() { echo $(($1 + $2))}function multiply() { echo $(($1 * $2))}

    現(xiàn)在奇瘦,我們可以在主腳本中使用source命令來引用這個模塊:

    #!/bin/bashsource ./math_functions.shresult_add=$(add 5 3)result_multiply=$(multiply 4 6)echo "5 + 3 = $result_add"echo "4 * 6 = $result_multiply"

    輸出:

    5 + 3 = 84 * 6 = 24

    3.2 使用點號(.)操作符

    點號操作符的功能與source命令相同,它是一個更簡潔的替代方案劲弦。

    示例2:使用點號引用模塊

    我們可以修改上面的主腳本耳标,使用點號來引用math_functions.sh

    #!/bin/bash. ./math_functions.shresult_add=$(add 10 7)result_multiply=$(multiply 3 9)echo "10 + 7 = $result_add"echo "3 * 9 = $result_multiply"

    輸出:

    10 + 7 = 173 * 9 = 27

    這兩種方法在功能上是等價的,選擇哪一種主要取決于個人偏好和腳本的可讀性邑跪。

    4. 創(chuàng)建和組織模塊

    有效的模塊化不僅僅是關(guān)于如何引用模塊次坡,更重要的是如何創(chuàng)建和組織這些模塊呼猪。讓我們探討幾種常見的模塊類型及其組織方式。

    4.1 函數(shù)模塊

    函數(shù)模塊是最常見的模塊類型砸琅,它們包含了可重用的函數(shù)定義宋距。

    示例3:創(chuàng)建字符串處理函數(shù)模塊

    # string_utils.sh#!/bin/bashfunction to_uppercase() { echo "$1" | tr '[:lower:]' '[:upper:]'}function to_lowercase() { echo "$1" | tr '[:upper:]' '[:lower:]'}function reverse_string() { echo "$1" | rev}

    使用這個模塊:

    #!/bin/bashsource ./string_utils.shoriginal="Hello, World!"upper=$(to_uppercase "$original")lower=$(to_lowercase "$original")reversed=$(reverse_string "$original")echo "Original: $original"echo "Uppercase: $upper"echo "Lowercase: $lower"echo "Reversed: $reversed"

    輸出:

    Original: Hello, World!Uppercase: HELLO, WORLD!Lowercase: hello, world!Reversed: !dlroW ,olleH

    4.2 變量模塊

    變量模塊用于存儲和共享配置信息或常用的數(shù)據(jù)結(jié)構(gòu)。

    示例4:創(chuàng)建配置變量模塊

    # config.sh#!/bin/bash# Database configurationDB_HOST="localhost"DB_PORT=3306DB_USER="admin"DB_PASS="secret"# API endpointsAPI_BASE_URL="https://api.example.com"API_VERSION="v1"# LoggingLOG_LEVEL="INFO"LOG_FILE="/var/log/myapp.log"

    使用配置模塊:

    #!/bin/bashsource ./config.shecho "Connecting to database at ${DB_HOST}:${DB_PORT}"echo "API URL: ${API_BASE_URL}/${API_VERSION}"echo "Logging to ${LOG_FILE} with level ${LOG_LEVEL}"

    輸出:

    Connecting to database at localhost:3306API URL: https://api.example.com/v1Logging to /var/log/myapp.log with level INFO

    4.3 常量模塊

    常量模塊用于定義在整個應(yīng)用中保持不變的值症脂。

    示例5:創(chuàng)建常量模塊

    # constants.sh#!/bin/bashreadonly MAX_RETRIES=3readonly TIMEOUT_SECONDS=30readonly ERROR_CODE_SUCCESS=0readonly ERROR_CODE_FAILURE=1

    使用常量模塊:

    #!/bin/bashsource ./constants.shattempt=1while [ $attempt -le $MAX_RETRIES ]; do echo "Attempt $attempt of $MAX_RETRIES" # 模擬某些操作 sleep 1 attempt=$((attempt + 1))doneif [ $attempt -gt $MAX_RETRIES ]; then echo "Operation failed after $MAX_RETRIES attempts" exit $ERROR_CODE_FAILUREelse echo "Operation succeeded" exit $ERROR_CODE_SUCCESSfi

    輸出:

    Attempt 1 of 3Attempt 2 of 3Attempt 3 of 3Operation failed after 3 attempts

    通過這種方式組織模塊谚赎,我們可以使主腳本更加清晰,同時提高代碼的可維護性和可重用性诱篷。

    5. 高級模塊引用技巧

    在實際的Shell腳本開發(fā)中沸版,我們經(jīng)常需要處理更復(fù)雜的模塊引用場景。本節(jié)將介紹一些高級技巧兴蒸,幫助您更靈活地管理和使用模塊。

    5.1 相對路徑和絕對路徑

    在引用模塊時细办,我們可以使用相對路徑或絕對路徑橙凳。選擇哪種方式取決于您的項目結(jié)構(gòu)和腳本的預(yù)期用途。

    示例6:使用相對路徑和絕對路徑

    假設(shè)我們有以下項目結(jié)構(gòu):

    /home/user/project/├── main.sh├── lib/│   ├── math.sh│   └── string.sh└── config/    └── settings.sh

    main.sh中笑撞,我們可以這樣引用模塊:

    #!/bin/bash# 使用相對路徑source ./lib/math.shsource ./lib/string.sh# 使用絕對路徑source /home/user/project/config/settings.sh# 使用腳本所在目錄的相對路徑SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"source "$SCRIPT_DIR/lib/math.sh"

    5.2 動態(tài)模塊加載

    有時岛啸,我們可能需要根據(jù)運行時的條件來決定加載哪些模塊。這可以通過使用變量來實現(xiàn)動態(tài)模塊加載茴肥。

    示例7:動態(tài)模塊加載

    #!/bin/bashMODULE_PATH="./modules"MODULES=("math" "string" "file")for module in "${MODULES[@]}"; do if [ -f "$MODULE_PATH/${module}.sh" ]; then source "$MODULE_PATH/${module}.sh" echo "Loaded module: $module" else echo "Warning: Module $module not found" fidone# 使用加載的模塊if type add &>/dev/null; then result=$(add 5 3) echo "5 + 3 = $result"else echo "Math module not loaded"fi

    這個腳本會嘗試加載modules目錄下的所有指定模塊坚踩,并在成功加載后使用其中的函數(shù)。

    5.3 條件模塊加載

    在某些情況下瓤狐,我們可能只想在特定條件下加載某些模塊瞬铸。這可以通過條件語句來實現(xiàn)。

    示例8:條件模塊加載

    #!/bin/bashENABLE_ADVANCED_FEATURES=truesource ./basic_functions.shif [ "$ENABLE_ADVANCED_FEATURES" = true ]; then source ./advanced_functions.sh echo "Advanced features enabled"else echo "Running with basic features only"fi# 使用函數(shù)basic_functionif type advanced_function &>/dev/null; then advanced_functionfi

    這個腳本根據(jù)ENABLE_ADVANCED_FEATURES變量的值來決定是否加載高級功能模塊础锐。

    6. 模塊化最佳實踐

    為了充分發(fā)揮模塊化的優(yōu)勢嗓节,遵循一些最佳實踐是非常重要的。這些實踐可以幫助您創(chuàng)建更易于維護和使用的模塊皆警。

    6.1 命名約定

    采用一致的命名約定可以大大提高代碼的可讀性和可維護性拦宣。

    示例9:模塊和函數(shù)命名約定

    # 文件名:string_utils.sh# 前綴函數(shù)名以避免命名沖突string_to_uppercase() { echo "${1^^}"}string_to_lowercase() { echo "${1,,}"}string_capitalize() { echo "${1^}"}

    在主腳本中使用:

    #!/bin/bashsource ./string_utils.shtext="hello WORLD"echo "Original: $text"echo "Uppercase: $(string_to_uppercase "$text")"echo "Lowercase: $(string_to_lowercase "$text")"echo "Capitalized: $(string_capitalize "$text")"

    輸出:

    Original: hello WORLDUppercase: HELLO WORLDLowercase: hello worldCapitalized: Hello WORLD

    6.2 文檔和注釋

    良好的文檔和注釋可以幫助其他開發(fā)者(包括未來的你)理解和使用你的模塊。

    示例10:模塊文檔和函數(shù)注釋

    #!/bin/bash# File: math_advanced.sh# Description: Advanced mathematical operations for shell scripts# Author: Your Name# Date: 2024-10-18# Calculate the factorial of a number# Args:# $1 - The number to calculate factorial for# Returns:# The factorial of the input numberfactorial() { local num=$1 local result=1 for ((i=2; i<=num; i++)); do result=$((result * i)) done echo $result}# Calculate the nth Fibonacci number# Args:# $1 - The position in the Fibonacci sequence# Returns:# The Fibonacci number at the specified positionfibonacci() { local n=$1 if [ $n -le 1 ]; then echo $n else local a=0 local b=1 for ((i=2; i<=n; i++)); do local temp=$((a + b)) a=$b b=$temp done echo $b fi}

    6.3 版本控制

    對模塊進行版本控制可以幫助管理依賴關(guān)系和兼容:

    #!/bin/bash# File: math_advanced.sh# Description: Advanced mathematical operations for shell scripts# Author: Your Name# Date: 2024-10-18# Calculate the factorial of a number# Args:# $1 - The number to calculate factorial for# Returns:# The factorial of the input numberfactorial() { local n=$1 if ((n <= 1)); then echo 1 else echo $((n * $(factorial $((n - 1))))) fi}# Calculate the nth Fibonacci number# Args:# $1 - The position in the Fibonacci sequence# Returns:# The nth Fibonacci numberfibonacci() { local n=$1 if ((n <= 1)); then echo $n else echo $(($(fibonacci $((n - 1))) + $(fibonacci $((n - 2))))) fi}

    使用這個模塊:

    #!/bin/bashsource ./math_advanced.shecho "Factorial of 5: $(factorial 5)"echo "10th Fibonacci number: $(fibonacci 10)"

    輸出:

    Factorial of 5: 12010th Fibonacci number: 55

    6.3 版本控制

    對模塊進行版本控制可以幫助管理依賴關(guān)系和跟蹤變更信姓。

    示例11:模塊版本控制

    在每個模塊文件的開頭鸵隧,添加版本信息:

    # File: string_utils.sh# Version: 1.2.0VERSION="1.2.0"# ... 函數(shù)定義 ...# 獲取模塊版本get_version() { echo $VERSION}

    在主腳本中檢查版本:

    #!/bin/bashsource ./string_utils.shrequired_version="1.1.0"current_version=$(get_version)if [[ "$(printf '%s\n' "$required_version" "$current_version" | sort -V | head -n1)" = "$required_version" ]]; then echo "String utils module version $current_version is compatible"else echo "Error: String utils module version $current_version is not compatible. Required version: $required_version" exit 1fi# ... 使用模塊功能 ...

    7. 常見問題和解決方案

    在使用模塊化Shell腳本時,可能會遇到一些常見問題意推。讓我們探討這些問題及其解決方案豆瘫。

    7.1 循環(huán)依賴

    循環(huán)依賴發(fā)生在兩個或多個模塊相互依賴的情況下。

    示例12:解決循環(huán)依賴

    假設(shè)我們有兩個相互依賴的模塊:

    # module_a.shsource ./module_b.shfunction_a() { echo "Function A" function_b}# module_b.shsource ./module_a.shfunction_b() { echo "Function B" function_a}

    解決方案:重構(gòu)代碼以消除循環(huán)依賴左痢,或使用主腳本來管理依賴:

    # main.shsource ./module_a.shsource ./module_b.shfunction_afunction_b

    7.2 命名沖突

    當(dāng)多個模塊定義相同名稱的函數(shù)或變量時靡羡,可能會發(fā)生命名沖突系洛。

    示例13:避免命名沖突

    使用命名空間或前綴來避免沖突:

    # math_module.shmath_add() { echo $(($1 + $2))}# string_module.shstring_add() { echo "$1$2"}# main.shsource ./math_module.shsource ./string_module.shecho "Math add: $(math_add 5 3)"echo "String add: $(string_add "Hello" "World")"

    7.3 性能考慮

    過度使用模塊可能會影響腳本的性能,特別是在處理大量小函數(shù)時略步。

    示例14:優(yōu)化模塊加載

    使用延遲加載技術(shù):

    #!/bin/bash# 延遲加載函數(shù)load_module() { if [ -z "$MODULE_LOADED" ]; then source ./heavy_module.sh MODULE_LOADED=true fi}# 包裝函數(shù)heavy_function() { load_module _heavy_function "$@"}# 使用函數(shù)heavy_function arg1 arg2

    8. 實戰(zhàn)項目:構(gòu)建模塊化的Shell應(yīng)用

    讓我們通過一個實際的項目來綜合應(yīng)用我們所學(xué)的知識描扯。我們將創(chuàng)建一個簡單的日志分析工具,它由多個模塊組成趟薄。

    項目結(jié)構(gòu):

    log_analyzer/├── main.sh├── modules/│   ├── file_utils.sh│   ├── log_parser.sh│   └── report_generator.sh└── config.sh

    config.sh:

    #!/bin/bash# Configuration file for log analyzer# Log file pathLOG_FILE="/var/log/app.log"# Report output directoryREPORT_DIR="./reports"# Log patternsERROR_PATTERN="ERROR"WARNING_PATTERN="WARNING"# Report format (text or html)REPORT_FORMAT="html"

    modules/file_utils.sh:

    #!/bin/bash# File utility functions# Check if a file exists and is readablefile_check_readable() { if [[ -r "$1" ]]; then return 0 else echo "Error: File '$1' does not exist or is not readable." >&2 return 1 fi}# Create directory if it doesn't existfile_ensure_dir() { if [[ ! -d "$1" ]]; then mkdir -p "$1" echo "Created directory: $1" fi}

    modules/log_parser.sh:

    #!/bin/bash# Log parsing functions# Count occurrences of a pattern in a filelog_count_pattern() { local file="$1" local pattern="$2" grep -c "$pattern" "$file"}# Extract lines matching a patternlog_extract_lines() { local file="$1" local pattern="$2" grep "$pattern" "$file"}

    modules/report_generator.sh:

    #!/bin/bash# Report generation functions# Generate HTML reportreport_generate_html() { local output_file="$1" local error_count="$2" local warning_count="$3" local error_lines="$4" local warning_lines="$5" cat << EOF > "$output_file"<html><head><title>Log Analysis Report</title></head><body><h1>Log Analysis Report</h1><p>Error Count: $error_count</p><p>Warning Count: $warning_count</p><h2>Error Lines:</h2><pre>$error_lines</pre><h2>Warning Lines:</h2><pre>$warning_lines</pre></body></html>EOF echo "HTML report generated: $output_file"}# Generate text reportreport_generate_text() { local output_file="$1" local error_count="$2" local warning_count="$3" local error_lines="$4" local warning_lines="$5" cat << EOF > "$output_file"Log Analysis Report===================Error Count: $error_countWarning Count: $warning_countError Lines:$error_linesWarning Lines:$warning_linesEOF echo "Text report generated: $output_file"}

    main.sh:

    #!/bin/bash# Main script for log analyzer# Source configuration and modulessource ./config.shsource ./modules/file_utils.shsource ./modules/log_parser.shsource ./modules/report_generator.sh# Check if log file exists and is readableif ! file_check_readable "$LOG_FILE"; then exit 1fi# Ensure report directory existsfile_ensure_dir "$REPORT_DIR"# Parse log fileerror_count=$(log_count_pattern "$LOG_FILE" "$ERROR_PATTERN")warning_count=$(log_count_pattern "$LOG_FILE" "$WARNING_PATTERN")error_lines=$(log_extract_lines "$LOG_FILE" "$ERROR_PATTERN")warning_lines=$(log_extract_lines "$LOG_FILE" "$WARNING_PATTERN")# Generate reporttimestamp=$(date +"%Y%m%d_%H%M%S")report_file="$REPORT_DIR/report_$timestamp.$REPORT_FORMAT"if [[ "$REPORT_FORMAT" == "html" ]]; then report_generate_html "$report_file" "$error_count" "$warning_count" "$error_lines" "$warning_lines"else report_generate_text "$report_file" "$error_count" "$warning_count" "$error_lines" "$warning_lines"fiecho "Log analysis complete. Report generated at $report_file"

    這個項目展示了如何使用模塊化方法來構(gòu)建一個更復(fù)雜的Shell應(yīng)用绽诚。它包含了配置管理、文件操作杭煎、日志解析和報告生成等功能恩够,每個功能都被封裝在獨立的模塊中,使得代碼更易于維護和擴展羡铲。

    9. 總結(jié)

    在本文中蜂桶,我們深入探討了Shell腳本中的模塊引用技術(shù)。我們學(xué)習(xí)了基本的模塊引用方法也切,如何創(chuàng)建和組織不同類型的模塊扑媚,以及一些高級的模塊引用技巧。我們還討論了模塊化編程的最佳實踐雷恃,包括命名約定疆股、文檔和注釋,以及版本控制倒槐。
    通過實戰(zhàn)項目旬痹,我們看到了如何將這些概念應(yīng)用到實際的腳本開發(fā)中,創(chuàng)建一個模塊化讨越、可維護的Shell應(yīng)用两残。
    模塊化不僅可以提高代碼的可讀性和可維護性,還能促進代碼重用把跨,提高開發(fā)效率磕昼。然而,在使用模塊化方法時节猿,我們也需要注意避免過度模塊化導(dǎo)致的復(fù)雜性增加票从,并始終關(guān)注性能優(yōu)化。隨著您在Shell腳本開發(fā)中積累更多經(jīng)驗滨嘱,您將能夠更好地平衡模塊化帶來的好處和潛在的挑戰(zhàn)峰鄙,創(chuàng)建出更加健壯和高效的腳本。

    本文使用 文章同步助手 同步

    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
    • 序言:七十年代末太雨,一起剝皮案震驚了整個濱河市吟榴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囊扳,老刑警劉巖吩翻,帶你破解...
      沈念sama閱讀 221,198評論 6 514
    • 序言:濱河連續(xù)發(fā)生了三起死亡事件兜看,死亡現(xiàn)場離奇詭異,居然都是意外死亡狭瞎,警方通過查閱死者的電腦和手機细移,發(fā)現(xiàn)死者居然都...
      沈念sama閱讀 94,334評論 3 398
    • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熊锭,“玉大人弧轧,你說我怎么就攤上這事⊥胍螅” “怎么了精绎?”我有些...
      開封第一講書人閱讀 167,643評論 0 360
    • 文/不壞的土叔 我叫張陵,是天一觀的道長锌妻。 經(jīng)常有香客問我代乃,道長,這世上最難降的妖魔是什么仿粹? 我笑而不...
      開封第一講書人閱讀 59,495評論 1 296
    • 正文 為了忘掉前任襟己,我火速辦了婚禮,結(jié)果婚禮上牍陌,老公的妹妹穿的比我還像新娘。我一直安慰自己员咽,他們只是感情好毒涧,可當(dāng)我...
      茶點故事閱讀 68,502評論 6 397
    • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贝室,像睡著了一般契讲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滑频,一...
      開封第一講書人閱讀 52,156評論 1 308
    • 那天捡偏,我揣著相機與錄音,去河邊找鬼峡迷。 笑死银伟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绘搞。 我是一名探鬼主播彤避,決...
      沈念sama閱讀 40,743評論 3 421
    • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夯辖!你這毒婦竟也來了琉预?” 一聲冷哼從身側(cè)響起,我...
      開封第一講書人閱讀 39,659評論 0 276
    • 序言:老撾萬榮一對情侶失蹤蒿褂,失蹤者是張志新(化名)和其女友劉穎圆米,沒想到半個月后卒暂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
      沈念sama閱讀 46,200評論 1 319
    • 正文 獨居荒郊野嶺守林人離奇死亡娄帖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
      茶點故事閱讀 38,282評論 3 340
    • 正文 我和宋清朗相戀三年也祠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片块茁。...
      茶點故事閱讀 40,424評論 1 352
    • 序言:一個原本活蹦亂跳的男人離奇死亡齿坷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出数焊,到底是詐尸還是另有隱情永淌,我是刑警寧澤,帶...
      沈念sama閱讀 36,107評論 5 349
    • 正文 年R本政府宣布佩耳,位于F島的核電站遂蛀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏干厚。R本人自食惡果不足惜李滴,卻給世界環(huán)境...
      茶點故事閱讀 41,789評論 3 333
    • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛮瞄。 院中可真熱鬧所坯,春花似錦、人聲如沸挂捅。這莊子的主人今日做“春日...
      開封第一講書人閱讀 32,264評論 0 23
    • 文/蒼蘭香墨 我抬頭看了看天上的太陽闲先。三九已至状土,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伺糠,已是汗流浹背蒙谓。 一陣腳步聲響...
      開封第一講書人閱讀 33,390評論 1 271
    • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留训桶,地道東北人累驮。 一個月前我還...
      沈念sama閱讀 48,798評論 3 376
    • 正文 我出身青樓,卻偏偏與公主長得像舵揭,于是被迫代替她去往敵國和親慰照。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
      茶點故事閱讀 45,435評論 2 359

    推薦閱讀更多精彩內(nèi)容