亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

shell編程:對話 UNIX: 更多 shell 腳本技術

系統(tǒng) 1660 0

盡管在近兩年使用過 UNIX 的一些人可能嘗試過 shell 腳本編程,但是他們很可能只是研究操作系統(tǒng)的細節(jié),并不精通 shell 腳本編程。本文針對那些希望進一步了解 shell 腳本,并開始編寫更高級腳本的讀者。本文提供腳本編程的基礎知識,包括如何簡化腳本、如何盡可能保持腳本的靈活性、如何編寫干凈的腳本、在腳本內(nèi)編寫注釋 以及調(diào)試腳本。

?

保持簡單

在人們學習如何編寫 shell 腳本時,常常遇到的一個問題是,重復他們在另一個腳本中已經(jīng)做過的工作。他們其實不需要復制原來的腳本并修改幾個硬編碼值,只需創(chuàng)建一個函數(shù)來處理兩個腳 本的重復部分。創(chuàng)建集中的函數(shù)還可以促進標準化,幫助創(chuàng)建統(tǒng)一的腳本。如果一個函數(shù)在腳本的一個部分工作正常,那么它在腳本中的其他地方也會正常工作。

?

?

例如,清單 1 所示的腳本應該濃縮和簡化為更簡單、更干凈的程序。

?
清單 1. 可以簡化的腳本示例
      #!/usr/bin/ksh

if [[ $# -lt 2 ]]
then
  echo "Usage: ${0##*/} <file name #1> <file name #2>
  exit 0
fi

if [[ ! -f "${1}" ]]
then
  echo "Unable to find file '${1}'"
  exit 1
fi

if [[ ! -r "${1}" ]]
then
  echo "Unable to read file '${1}'"
  exit 2
fi

gzip ${1}
ls -l ${1}.gz


if [[ ! -f "${2}" ]]
then
  echo "Unable to find file '${2}'"
  exit 1
fi

if [[ ! -r "${2}" ]]
then
  echo "Unable to read file '${2}'"
  exit 2
fi

gzip ${2}
ls -l ${2}.gz
    

?

這個腳本看起來很糟糕!(謝天謝地,它只是一個示例)。這個腳本應該盡可能進行濃縮。從便于閱讀的角度來看,清單 2 提供的版本更干凈。

?
清單 2. 對清單 1 腳本進行濃縮的版本
      #!/usr/bin/ksh

exit_msg() {
  [[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
  exit ${1:-0}
}

[[ $# -lt 2 ]] && exit_msg 0 "Usage: ${0##*/} <file name #1> <file name #2>

for _FNAME in $@
do
  [[ ! -f "${_FNAME}" ]] && exit_msg 1 "Unable to find file '${_FNAME}'"
  [[ ! -r "${_FNAME}" ]] && exit_msg 2 "Unable to read file '${_FNAME}'"

  gzip ${_FNAME}
  ls -l ${_FNAME}.gz
done
    

?

注意到這兩者的差異了嗎?這個腳本增加了一個簡單的函數(shù)來顯示一個消息并帶適當?shù)姆祷卮a退出,還把所有操作轉(zhuǎn)移到一個 for 循環(huán)中,這使這個腳本看起來更干凈、更容易理解了。

?

保持靈活性

編程和 shell 腳本編程的新手常常犯的另一個錯誤是,在程序或 shell 腳本中對靜態(tài)值進行硬編碼。這會限制腳本的靈活性,是一種糟糕的編程習慣。這迫使管理員或開發(fā)人員不得不經(jīng)常修改腳本以使用其他值;為了避免這個問題,應該使用變量并為腳本或函數(shù)提供參數(shù)。

?

例如,清單 3 是一個編寫得很差的不靈活的示例腳本。

?
清單 3. 不靈活的示例腳本
      #!/bin/bash

if [[ -f /home/cormany/FileA ]]
then
  echo "Found file '/home/cormany/FileA'"
elif [[ -f /home/cormany/DirA/FileA ]]
then
  echo "Found file '/home/cormany/DirA/FileA'"
else
  echo "Unable to find file FileA"
fi
    

?

這個腳本可以正常工作,但是它只能在兩個位置搜索一個文件。

清單 4 提供相同的功能,但是允許用戶在任何位置搜索任何文件。

?
清單 4. 使腳本更靈活
      #!/bin/bash

exit_msg() {
  [[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
  exit ${1:-0}
}

[[ $# -lt 2 ]] && exit_msg 1 "Usage: ${0##*/} <file name> <location>"

_FNAME="${1}"
_DNAME="${2}"

[[ ! -d "${_DNAME}" ]] && exit_msg 2 "Unable to read or find directory '${_DNAME}'"

if [[ -f "${_DNAME}/${_FNAME}" ]]
then
  exit_msg 0 "Found file '${_DNAME}/${_FNAME}'"
else
  exit_msg 3 "Unable to find file '${_DNAME}/${_FNAME}'"
fi
    

?

這個腳本更靈活,因為它允許用戶指定要搜索的任何文件和任何搜索目錄。

?

提供選項

在編寫一個 shell 腳本時,一些用戶可能會說,“它真不錯!” 或者 “我喜歡使用它”;而同時,其他用戶可能不同意這個評價,他們可能不希望執(zhí)行相同的操作。人們喜歡有選擇,為什么不給他們提供選項呢?內(nèi)置的 shell 命令 getopt 可以完成這個任務。

?

清單 5 提供一個在 AIX 中使用 getopt 的基本示例。

?
清單 5. getopt 示例
      #!/usr/bin/ksh

_ARGS=`getopt -o x --long xxxxx -n ${0##*/} -- "$@"`
while [[ $# -gt 0 ]]
do
  case "${1}" in
    -x|--xxxxx)  echo "Arg x hit!"; shift;;
            --)  shift; break;;
             *)  echo "Invalid Option: ${1}"; break;;
  esac
done
    

?

在執(zhí)行包含 getopt (稱為 opttest )的腳本時,如果在 -x --xxxxx 中使用有效的參數(shù), getopt 會識別出開關并執(zhí)行 case 開關中的代碼:

      # ./hm -x
Arg x hit!
    

?

下面是使用無效開關或選項時的結(jié)果:

      # ./hm -a
Invalid Option: -a
    

?

文檔,文檔,文檔

我 們在職業(yè)生涯中早晚會受到這個問題的困擾。老板要求您看看一個 10 年前編寫的腳本,它的作者已經(jīng)不再為公司工作了。您會說 “沒問題” 嗎?通常情況下,可能沒問題;但是,如果這個腳本很復雜,執(zhí)行了您不習慣使用的命令,采用的編寫風格與您的風格不一樣,或者干脆就不能正常工作,您就遇到 大麻煩了。在這種情況下,一些反映作者當初編寫這個腳本時的想法的提示會有很大的幫助。有時候,您開發(fā)了一個自認為只使用一次的腳本,但是以后卻發(fā)現(xiàn)還需 要修改它。或者,您用幾星期時間編寫了一個巨大的腳本,您了解這個腳本的所有細節(jié),但是如果別人閱讀它,卻不知所云。這幾種情況說明,文檔之于開發(fā)人員就 像腳本之于用戶,都非常重要。

看看清單 6 所示的函數(shù)。

?
清單 6. 沒有注釋的腳本示例
      confirm_and_exit() {
  [[ ${_DEBUG_LEVEL} -ge 3 ]] && set -x
  while [[ -z ${_EXIT_ANS} ]]
  do
    cup_echo "Are you sure you want to exit? [Y/N]  
        \c" ${_PROMPT_ERR_ROW} ${_PROMPT_ERR_COL}
    ${_TPUT_CMD} cnorm
    read ${_NO_EOL_FLAG:+${_READ_FLAG:-'-n'}} ${_NO_EOL_FLAG} _EXIT_ANS
    ${_TPUT_CMD} civis
  done

  case ${_EXIT_ANS} in
    [Nn])  unset _EXIT_ANS; return 0;;
    [Yy])  exit_msg 0 1 "Exiting Script";;
       *)  invalid_selection ${_EXIT_ANS}; unset _EXIT_ANS;;
  esac
  return 0
}
    

?

如果您有比較豐富的 shell 腳本編程經(jīng)驗,可能能夠讀懂這個腳本。但是,腳本編程的初學者很難理解這個函數(shù)的作用。如果花上幾分鐘在這個腳本中添加注釋,情況就大不一樣了。清單 7 給出包含注釋的同一個函數(shù)。

?
清單 7. 包含注釋的腳本示例
      #########################################
# function confirm_and_exit
#########################################
confirm_and_exit() {
  # if the debug level is set to 3 or higher, send every evaluated line to stdout
  [[ ${_DEBUG_LEVEL} -ge 3 ]] && set –x

  # Continue to prompt the user until they provide a valid answer
  while [[ -z ${_EXIT_ANS} ]]
  do
    # prompt user if they want to exit the script
    # cup_echo function calls tput cup <x> <y>
    # syntax:
    # cup_echo <string to display> <row on stdout to display> 
        <column on stdout to display>
    cup_echo "Are you sure you want to exit? [Y/N]  
        \c" ${_PROMPT_ERR_ROW} ${_PROMPT_ERR_COL}

    # change cursor to normal via tput
    ${_TPUT_CMD} cnorm

    # read value entered by user
    # if _NO_EOL_FLAG is supplied, use value of _READ_FLAG or “-n”
    # if _NO_EOL_FLAG is supplied, use value as characters aloud on read
    # assign value entered by user to variable _EXIT_ANS
    read ${_NO_EOL_FLAG:+${_READ_FLAG:-'-n'}} ${_NO_EOL_FLAG} _EXIT_ANS

    # change cursor to invisible via tput
    ${_TPUT_CMD} civis
  done

  # if user entered “n”, return to previous block of code with return code 0
  # if user entered “y”, exit the script
  # if user entered anything else, execute function invalid_selection
  case ${_EXIT_ANS} in
    [Nn])  unset _EXIT_ANS; return 0;;
    [Yy])  exit_msg 0 1 "Exiting Script";;
       *)  invalid_selection ${_EXIT_ANS}; unset _EXIT_ANS;;
  esac

  # exit function with return code 0
  return 0
}
    

?

對于這么小的函數(shù),這似乎太麻煩了,甚至有點過分,但是對于 shell 腳本編程新手和閱讀這個函數(shù)的人員而言,注釋是非常有價值的。

?

?

在 shell 腳本中,注釋的另一個極其有幫助的用途是,解釋變量的有效值以及解釋返回碼的含義。

清單 8 中的示例取自一個 shell 腳本的開頭。

?
清單 8. 未加注釋的變量示例
      #!/usr/bin/bash
trap 'exit_msg 1 0 "Signal Caught. Exiting..."' HUP INT QUIT KILL ABRT
trap 'window_size_changed' WINCH

_MSG_SLEEP_TIME=3
_RETNUM_SIZE=6
_DEBUG_LEVEL=0
_TMPDIR="/tmp"
_SP_LOG="${0##*/}.log"
_SP_REQUESTS="${HOME}/sp_requests"
_MENU_ITEMS=15
LESS="-P LINE\: %l"
export _SP_REQUESTS _TMPDIR _SP_LOG _DB_BACKUP_DIR 
    export _DEBUG_LEVEL _NEW_RMSYNC _RMTOTS_OFFSET_COL
    

?

同樣,很難理解 trap 語句的作用以及每個變量可以是哪些值。除非把整個腳本都讀一遍,否則不可能看出這些變量的意義。另外,這里沒有提到這個腳本中使用的任何返回碼。這會大大增加解決 shell 腳本問題的難度。向 清單 8 的代碼行中添加一些注釋和一個專門描述返回碼的注釋塊,這樣就可以顯著降低理解難度。看看下面的清單 9。

?
清單 9. 帶注釋的變量示例
      #!/usr/bin/bash
#########################################################################
# traps
#########################################################################
# trap when a user is attempting to leave the script
trap 'exit_msg 1 0 "Signal Caught. Exiting..."' HUP INT QUIT KILL ABRT
trap 'window_size_changed' WINCH                # trap when a user has resized the window
#########################################################################

#########################################################################
# defined/exported variables
#########################################################################
_MSG_SLEEP_TIME=3               # seconds to sleep for all messages
                                      # (if not defined, default will is 1 second)
_CUSTNUM_SIZE=6                   # length of a customer number in this location
                                      # (if not defined, default is 6)
_DEBUG_LEVEL=0                      # log debug messages. log level is accumulative
                                      # (i.e. 1 = 1, 2 = 1 & 2, 3 = 1, 2, & 3)
                                      # (if not defined, default is 0)
                                      # Log levels:
                                      #  0 = No messages
                                      #  1 = brief messages (start script, errors, etc)
                                      #  2 = environment setup (set / env)
                                      #  3 = set -x (A LOT of spam)
_TMPDIR="/tmp"          # directory to put work/tmp files
                                      # (if not defined, default is /tmp)
_SP_LOG="${0##*/}.log"   # log of script events
_SP_REQUESTS="${HOME}/sp_requests"
			# file to customer record requests,
                                       # also read at startup
_MENU_ITEMS=15        # default number of items to display per page
                                       # (it not defined, default is 10)
LESS="-P LINE\: %l"        # format 'less' prompt. MAN less if more info


# export the variables defined above
export _MSG_SLEEP_TIME _CUSTNUM_SIZE _DEBUG_LEVEL _TMPDIR 
    _SP_LOG _SP_REQUESTS _MENU_ITEMS
#########################################################################
    

?

看起來好多了,不是嗎?所有東西都組織有序,并且有詳細的描述,初次閱讀這個腳本的人更容易理解它的作用。

?

調(diào)試

編 寫完一個腳本之后,就要第一次運行它了。但是,如果在執(zhí)行腳本時顯示某些意外的錯誤,應該怎么辦呢?沒有人是完美的,而且從頭編寫腳本并保持沒有錯誤需要 大量時間和豐富的經(jīng)驗;大多數(shù)時候,開發(fā)人員很容易漏掉一個字母或者顛倒了兩個字母的順序,這幾乎是不可避免的。不必擔心:AIX、其他風格的 UNIX 和 Linux 中的 shell 已經(jīng)考慮到了這個問題,可以幫助您進行調(diào)試。

?

例如,清單 10 中的 shell 腳本(名為 make_errors )已經(jīng)編寫好等待執(zhí)行。

?
清單 10. 包含錯誤的腳本示例
      #!/bin/bash

_X=1
while [[ ${_X} -le 10 ]]
do
  [[ ${_X} -lt 5 ]] && echo "X is less than 5!

  _Y=`expr ${_X) + 1`

  if [[ ${_Y} -eq 6 ]]
    echo "Y is now equal to ${_Y}"
  fi

  _X=${_Y}
done
    

?

但是,初次執(zhí)行這個腳本時,顯示以下錯誤:

      # ./make_errors
./make_errors: line 11: unexpected EOF while looking for matching `"'
./make_errors: line 16: syntax error: unexpected end of file
    

?

Vim 是一種出色的調(diào)試工具,您可能使用過它,但不一定了解它的真正價值。Vim 是一種強大的文本編輯器,但是它對調(diào)試也很有幫助。如果通過設置 .exrc 或 .vimrc 文件指定用不同的顏色顯示某些錯誤,Vim 就會替您完成大部分調(diào)試工作,見圖 1。

?

圖 1. 用 Vim 進行調(diào)試

用 Vim 進行調(diào)試

第一個錯誤消息( line 11: unexpected EOF while looking for matching `"' )指出在第 11 行上有錯誤,但是看過這一行之后,并沒有發(fā)現(xiàn)任何錯誤。再看看第 9 行。 echo 后面的字符串的末尾缺少一個雙引號( " )。 這個示例很好地說明了在進行調(diào)試時為什么必須查看整個腳本。錯誤消息中顯示的行號不一定是出現(xiàn)錯誤的實際位置。報告第 11 行有錯誤是因為第 9 行用雙引號標出一個字符串的開頭,但是這個字符串直到第 11 行還沒有結(jié)束。要想糾正這個錯誤,應該在第 9 行末尾添加雙引號。

?

其他一些問題也會顯示為錯誤。在第 11 行上,變量值 _X 后面是一個用紅色突出顯示的后圓括號( ) )。這是 Vim 替您做出的判斷,它指出這里有錯誤。這里用一個前花括號( { )標出了變量值 _X 的開頭,但是沒有用后花括號( } )結(jié)束。只需把 ) 改為 } ,就能夠糾正這個錯誤。

?

到目前為止,已經(jīng)糾正了兩個錯誤。再次運行這個腳本,看看會發(fā)生什么:

      ./make_errors: line 12: syntax error near unexpected token `fi'
./make_errors: line 12: `  fi'
    

?

還有另一個錯誤。錯誤消息指出問題出現(xiàn)在第 12 行上,但是這一行只有一個用來結(jié)束 if 語句的 fi 。這有什么錯呢?請牢記前一個錯誤的情況。并非所有錯誤都源自 shell 所報告的行上。shell 僅僅報告發(fā)生錯誤的位置,但是錯誤的根源可能出現(xiàn)在這個位置之前。對于這個小腳本,可以很有把握地猜測錯誤可能出現(xiàn)在實際的 if 語句中。回憶一下基本的腳本編程邏輯: if 語句由 if then fi 組成。看看這個條件語句,可以看出缺少了 then 。只需在腳本中添加 then 。完成之后,這個腳本應該類似于清單 11。

?
清單 11. 糾正清單 10 中的錯誤之后的腳本
      #!/bin/bash

_X=1
while [[ ${_X} -le 10 ]]
do
  [[ ${_X} -lt 5 ]] && echo "X is less than 5!"

  _Y=`expr ${_X} + 1`

  if [[ ${_Y} -eq 6 ]]
  then
    echo "Y is now equal to ${_Y}"
  fi

  _X=${_Y}
done
    

?

再次運行這個腳本:

      # ./make_errors
X is less than 5!
X is less than 5!
X is less than 5!
X is less than 5!
Y is now equal to 6
    

?

恭喜!這個腳本現(xiàn)在正常工作了!

?

set -x 選項

有時候,對 shell 腳本執(zhí)行基本的錯誤排除步驟并不像前一個示例那么容易。如果所有努力都失敗了,并且想不出腳本的錯誤之處在哪里,那么最后一招就是動用 “殺手锏”!Ksh、Bash 和其他現(xiàn)代 shell 都支持在 set 命令中使用 -x 開關。如果使用 set –x 選項,執(zhí)行的每個命令都顯示在 stdout 中。為了突出顯示執(zhí)行的代碼, set –x 把 PS4 變量的值加在顯示的每行代碼前面。請記住,這種做法會產(chǎn)生大量文本,所以在查看輸出時要有耐心。

減小前一個示例中的循環(huán)計數(shù)值,在腳本的開頭添加 set -x 和一個注釋,見清單 12。

?
清單 12. set -x 示例
      #!/bin/bash

set -x

# loop through and display some test statements
_X=1
while [[ ${_X} -le 4 ]]
do
  [[ ${_X} -lt 2 ]] && echo "X is less than 2!

  _Y=`expr ${_X} + 1`

  if [[ ${_Y} -eq 3 ]]
  then
    echo "Y is now equal to ${_Y}"
  fi

  _X=${_Y}
done
    

?

在執(zhí)行這個腳本之前,把 PS4 變量改為某個看起來醒目的字符串:

      # export PS4="DEBUG => "
    

?

接下來,執(zhí)行這個腳本,就會看到可能非常有價值的信息,見清單 13。

?
清單 13. set -x 的輸出
      # ./make_errors
DEBUG => _X=1
DEBUG => [[ 1 -le 4 ]]
DEBUG => [[ 1 -lt 2 ]]
DEBUG => echo 'X is less than 2!'
X is less than 2!
DDEBUG => expr 1 + 1
DEBUG => _Y=2
DEBUG => [[ 2 -eq 3 ]]
DEBUG => _X=2
DEBUG => [[ 2 -le 4 ]]
DEBUG => [[ 2 -lt 2 ]]
DDEBUG => expr 2 + 1
DEBUG => _Y=3
DEBUG => [[ 3 -eq 3 ]]
DEBUG => echo 'Y is now equal to 3'
Y is now equal to 3
DEBUG => _X=3
DEBUG => [[ 3 -le 4 ]]
DEBUG => [[ 3 -lt 2 ]]
DDEBUG => expr 3 + 1
DEBUG => _Y=4
DEBUG => [[ 4 -eq 3 ]]
DEBUG => _X=4
DEBUG => [[ 4 -le 4 ]]
DEBUG => [[ 4 -lt 2 ]]
DDEBUG => expr 4 + 1
DEBUG => _Y=5
DEBUG => [[ 5 -eq 3 ]]
DEBUG => _X=5
DEBUG => [[ 5 -le 4 ]]
    

?

可以看到這里有大量信息:處理并執(zhí)行的每個命令都顯示出來了。還要注意,在調(diào)試信息中沒有顯示 shell 腳本中的注釋。這是因為 shell 在讀取注釋之后并不執(zhí)行它。還好,在完成前面的修改之后,這個腳本沒有錯誤了!

?

在使用 set -x 時還要記住一點:如果腳本有內(nèi)部函數(shù),而且 set -x 放在代碼的主體部分,那么它的輸出會包含子函數(shù)的運算過程。但是,如果 set -x 只放在內(nèi)部函數(shù)中,那么 debug 選項的影響范圍只包含這個內(nèi)部函數(shù)中的代碼和在其中調(diào)用的子函數(shù);shell 腳本的主體并不包含在內(nèi),這是因為它不知道它的內(nèi)部函數(shù)會調(diào)用這個例程。

?

結(jié)束語

無論是使用 shell 腳本、 C 、Java? 語言或其他語言,我們都在不斷地改進編程方法。堅持簡單化的基本規(guī)則,保持代碼簡潔靈活,給代碼加上適當?shù)淖⑨專俳柚{(diào)試工具的幫助,您很快就能編寫出出色的 shell 腳本。祝您好運!

?

?

參考資料

學習

  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文
  • 對話 UNIX :閱讀本系列的其他文章。
  • 關于 AIX 的 Wikipedia 文章 :閱讀關于 AIX 操作系統(tǒng)的 Wikipedia 文章,進一步了解 AIX 的背景知識和開發(fā)。
  • 關于 shell 腳本的 Wikipedia 文章 :閱讀關于 shell 腳本的 Wikipedia 文章,進一步了解 shell 腳本的概念和功能。
  • getopt 命令 :關于這個命令的更多信息請參考 IBM 的命令參考。
  • AIX and UNIX 專區(qū) :developerWorks 的“AIX and UNIX 專區(qū)”提供了大量與 AIX 系統(tǒng)管理的所有方面相關的信息,您可以利用它們來擴展自己的 UNIX 技能。
  • AIX and UNIX 新手入門 :訪問“AIX and UNIX 新手入門”頁面可了解更多關于 AIX 和 UNIX 的內(nèi)容。
  • AIX and UNIX 專題匯總 :AIX and UNIX 專區(qū)已經(jīng)為您推出了很多的技術專題,為您總結(jié)了很多熱門的知識點。我們在后面還會繼續(xù)推出很多相關的熱門專題給您,為了方便您的訪問,我們在這里為您把本專區(qū)的所有專題進行匯總,讓您更方便的找到您需要的內(nèi)容。
  • developerWorks 技術活動和網(wǎng)絡廣播 :了解最新的 developerWorks 技術活動和網(wǎng)絡廣播。
  • AIX Wiki :在這個協(xié)作性環(huán)境中可以找到關于 AIX 的技術信息。
  • Podcasts :收聽 Podcast 并了解 IBM 技術專家的最新想法。

?

獲得產(chǎn)品和技術

  • IBM 試用軟件 :使用可從 developerWorks 直接下載試用軟件構建您的下一個開發(fā)項目。

?

討論

?

來源: http://www.ibm.com/developerworks/cn/aix/library/au-speakingunix_shellscripttech/

?

?

shell編程:對話 UNIX: 更多 shell 腳本技術


更多文章、技術交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 亚洲视频中文 | 黄色在线观看视频免费 | 九九热精品在线视频 | 亚洲欧美一区二区三区在饯 | 久久精品视频大全 | 天天插夜夜爽 | 欧美精品专区免费观看 | 欧美手机手机在线视频一区 | 女人十八毛片免费特黄 | 中文字幕在线高清 | 亚洲午夜网 | 日本高清一级做a爱过程免费视频 | 日本一区二区三区在线 观看网站 | 按摩理论片 | 国产成人精品999在线 | 爱爱小视频免费体验区在线观看 | 日日摸夜夜爽 | 国产精品国产国产aⅴ | 日韩字幕一中文在线综合 | 亚洲九九香蕉 | 中文字幕 亚洲精品 第1页 | 成人午夜久久精品 | 日韩欧美小视频 | 久久草视频在线 | 久久爱www人成 | 久久免费在线 | 精品国产一区二区三区四 | 国产成人亚洲综合91精品555 | 亚洲加勒比久久88色综合一区 | 亚洲综合图色 | 欧美亚洲国产成人精品 | 欧美日韩久久毛片 | 国产日韩亚洲欧洲一区二区三区 | 欧美精品99 | 夜色福利一区二区三区 | 成人a级特黄毛片 | 国产成人免费在线 | 综合亚洲一区二区三区 | 五月婷婷婷婷婷 | 欧美顶级毛片在线播放 | 一a一级片 |