影音數位典藏所需的自動化腳本(Bash Script)--函式

影音數位典藏所需的自動化腳本(Bash Script)--函式


    在撰寫腳本和程式時,有時候會過到相同的工作會同時出現在不同階段的狀況,例如顯示提示或錯誤訊息。此時,「函式 ( 或稱函數,子程式,function/subroutine)」就是一個好用的工具。如果同樣的功能會在不同腳本中都會用到,例如判斷輸入檔案,也可以寫成函式。在撰寫新的腳本中,可以直接將整個函式整個搬過去,就不用再寫一次了。如果常用的函式累積得夠多,還可以直接整理到一個檔案中,做成函式庫。直接透過引用 (source) 來呼叫函,連複製的動作都可以省略。

函式的完整表示方式為:

function fname() {
    command
}

其中 fname 就是未來要做成執行指令的名稱,而 command 就是這段指令預期執行的內容。與一般程式寫法不同,腳本有嚴格的前後順序。因此在腳本中的所有函式必需放在主程式段之前,否則會有無法找到函式的問題。

利用函式撰寫產生 MD5 的腳本

    前面寫過產生影音檔 metadata 的腳本,相同的架構可以很外的修改成產生以畫格為基礎的 md5 集的功能,只要將最後產生 json 的指令更改如下即可:
ffmpeg -i "${input_file}" -f framemd5 "${filename}"

但這樣的腳本中有許多部份可以拆成下列幾個部份:

  • 顯示錯誤訊息
  • 顯示操作說明
  • 讀取目標
  • 判斷目標是否為影音檔
  • 執行主要指令

  1. 顯示錯誤訊息
    # display an error message and exit
    function prompt_error() {
        echo "Error: ${1:-Unknown Error.}"
        exit 1
    }
    建立一個名為 prompt_error 的函式,這個函式的工作是顯示 "Error: 錯誤訊息"。 因此在後續的指令中,需要顯示錯誤訊息的地方,只要寫成 「prompt_error "錯誤訊息"」 即可。「${1:-Unknown Error.} 」這個寫法代表,應出現在指令後方 (位置 $1 ) 的錯誤訊息如果不存在,則直接使用 "Unknown Error." 取代。
    由於出現錯誤訊息後會需要直接離開程式,因此以 「exit 1」,表有錯誤的中止腳本。
  2. 顯示操作說明
    為了避免久未使用而忘記,或腳本會提供給其他使用者,通常會在腳本中提示基本的操作方式或說明文件,通常會透過 -h 或 -help 的參數來做為顯示說明的方式。因此建立一個名為 prompt_help 的函式,這個函式的工作是顯示操作說明。
    function prompt_help() {
        script_name="$(basename "${0}")"
        # 定義變數 script_name 為腳本名稱
        # 使用 basename 去除
        cat <<EOF
        # 使用 cat 指令來顯示下列字串,直到 EOF 為止

        # 由此開始到 EOF 前為顯示的資訊
    Usage:
      ${script_name}
      ${script_name} FILE
      ${script_name} -h
    EOF
        exit 0
        # 由於顯示說明是這個腳本正常的結束狀態,因此以 exit 0 結尾
    }
  3. 讀取目標
    腳本的主要指令區段從讀取目標開始,首先使用一個 if 判斷式來將目標讀入變數 input_file 中。如果輸入為 "-h" 時,則顯示說明文件。
    # Main section ########
    # read input
    if (( "${#}" == 0 )); then
        echo -n "Please drag-and-drop a file: "
        read input_file
    else
        input_file="${1}"
    fi

        # 若無輸入檔名時顯示提示,若有檔名時則定義變數

    if [[ "${input_file}" = "-h" ]]; then
        prompt_help
    fi

        # 若輸入值為 "-h" 時,執行 prompt_help 這個函式
    由於這段開始使用到 prompt_help 這個函式,因此在撰寫時需注意,函式一定要在這段落前出現。
  4. 判斷目標是否為影音檔
    前文所寫的 json.sh 中的判斷目標是否為影音檔類似,但由於每次判斷後都要輸出錯誤訊息,這樣的功能可用前面準備好的 prompt_error 函式來取代:
    # verify input
    if [[ "${#}" -gt 1 ]]; then
        prompt_error "too many arguments: ${#}."
        # 判斷是否輸入過多變數
    elif [[ ! -f "${input_file}" ]]; then
        prompt_error "'${input_file}' is not a file."
        # 判斷輸入目標是否為檔案
    elif [[ "${input_file}" = "$(basename ${0})" ]]; then
        prompt_error "'$(basename ${input_file})' is... myself!"
        # 若輸入目標與腳本檔名相同,顯示錯誤訊息
    elif cat "${input_file}" | grep "#format: frame checksums" >/dev/null; then 
        prompt_error "'$(basename ${input_file})' is a frame MD5 checksum file."
        # 若輸入目標中有 
    "#format: frame checksums" 的字串
        # 代表該檔為預期輸出的 md5 檔
    elif ffprobe "${input_file}" 2>&1 | grep "Invalid data found" >/dev/null; then
        prompt_error "'$(basename ${input_file})' is not an AV file."
        # 判斷是否為影音檔
    fi
  5. 執行主要指令
    在確定輸入目標為符合預期的影音檔後,便可以執行想要的主要指令:
    # generate frame MD5 checksums 
    filename="${input_file%.*}_${input_file##*.}_md5.txt"
    ffmpeg -i "${input_file}" -f framemd5 "${filename}"
  
    實際執行這個腳本試試:
  1. 沒有輸入目標:
    bash-4.4$ ./md5.sh
    Please drag-and-drop a file: 

  2. 輸入「-h」顯示操作說明:
    bash-4.4$ ./md5.sh -h
    Usage:
      md5.sh
      md5.sh FILE
      md5.sh -h
  3. 輸入超過一個目標
    bash-4.4$ ./md5.sh md5.sh video.mp4 video_mp4_md5.txt 
    Error: too many arguments: 3.
  4. 輸入目標不是檔案:
    bash-4.4$ ./md5.sh error_file
    Error: 'error_file' is not a file.
  5. 輸入目標是腳本:
    bash-4.4$ ./md5.sh md5.sh 
    Error: 'md5.sh' is... myself!
  6. 輸入目標已是 MD5 檔案:
    bash-4.4$ ./md5.sh video_mp4_md5.txt 
    Error: 'video_mp4_md5.txt' is a frame MD5 checksum file.
  7. 輸入目標不是可接受的影音檔:
    bash-4.4$ ./md5.sh video_mp4.json 
    Error: 'video_mp4.json' is not an AV file.
  8. 輸入一個可接受的影音檔:
    bash-4.4$ ./md5.sh video.mp4 
    在一串程式輸出訊息後,可檢查是否有新增預期的目標檔案。
    bash-4.4$ ls *md5*
    md5.sh video_mp4_md5.txt
    可以透過 cat 指令檢查檔案內容,確定檔案中有每個畫格的 MD5 數值。這個數值可以在後續或日常監控。確保每個畫格在檔案複製或搬移過程中沒有任何漏碼或錯誤。
    0,        117,        117,        1,   115200, 308cbed68d0bc873c9dabd0bb533af01
    0,        118,        118,        1,   115200, a26e0120565fef2dcfd024c310b858d5
    0,        119,        119,        1,   115200, d019222dd7a29a2d810b863aa61e6d5f
透過函式(function)來建構腳本,可以讓腳本的結構和流程變得很清楚,讓日後的維護也更方便。下篇會回到判斷式,當已經預知會有某些狀況時,並不一定要使用 if 判斷式去逐一判斷,下篇會講解如何使用 case 判斷式來讓流程更簡明。

此文所使用的腳本與影片可由下列連結取得:
  • 未加註解原始腳本檔:md5.sh
  • 測試用影片檔:video.mp4 
  • 也可以利用 ffmpeg 產生測試影片:
    ffmpeg \-f lavfi -i testsrc2=duration=5:size=320x240:rate=24 \
    video.mp4

留言

這個網誌中的熱門文章

影音類載體 -- 聲音類 -- 8軌匣式錄音帶(8 track tape)

低成本的數位影像修復 (OpenCV Inpainting)