影音數位典藏所需的自動化腳本(Bash Script)--函式
影音數位典藏所需的自動化腳本(Bash Script)--函式
在撰寫腳本和程式時,有時候會過到相同的工作會同時出現在不同階段的狀況,例如顯示提示或錯誤訊息。此時,「函式 ( 或稱函數,子程式,function/subroutine)」就是一個好用的工具。如果同樣的功能會在不同腳本中都會用到,例如判斷輸入檔案,也可以寫成函式。在撰寫新的腳本中,可以直接將整個函式整個搬過去,就不用再寫一次了。如果常用的函式累積得夠多,還可以直接整理到一個檔案中,做成函式庫。直接透過引用 (source) 來呼叫函,連複製的動作都可以省略。
函式的完整表示方式為:
function fname() {
command
}
其中 fname 就是未來要做成執行指令的名稱,而 command 就是這段指令預期執行的內容。與一般程式寫法不同,腳本有嚴格的前後順序。因此在腳本中的所有函式必需放在主程式段之前,否則會有無法找到函式的問題。
利用函式撰寫產生 MD5 的腳本
前面寫過產生影音檔 metadata 的腳本,相同的架構可以很外的修改成產生以畫格為基礎的 md5 集的功能,只要將最後產生 json 的指令更改如下即可:
ffmpeg -i "${input_file}" -f framemd5 "${filename}"
但這樣的腳本中有許多部份可以拆成下列幾個部份:
ffmpeg -i "${input_file}" -f framemd5 "${filename}"
但這樣的腳本中有許多部份可以拆成下列幾個部份:
- 顯示錯誤訊息
- 顯示操作說明
- 讀取目標
- 判斷目標是否為影音檔
- 執行主要指令
- 顯示錯誤訊息
# 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」,表有錯誤的中止腳本。
- 顯示操作說明
為了避免久未使用而忘記,或腳本會提供給其他使用者,通常會在腳本中提示基本的操作方式或說明文件,通常會透過 -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 結尾
}
- 讀取目標
腳本的主要指令區段從讀取目標開始,首先使用一個 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 這個函式,因此在撰寫時需注意,函式一定要在這段落前出現。
- 判斷目標是否為影音檔
與前文所寫的 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
- 執行主要指令
在確定輸入目標為符合預期的影音檔後,便可以執行想要的主要指令:
# generate frame MD5 checksums
filename="${input_file%.*}_${input_file##*.}_md5.txt"
ffmpeg -i "${input_file}" -f framemd5 "${filename}"
實際執行這個腳本試試:
- 沒有輸入目標:
bash-4.4$ ./md5.sh
Please drag-and-drop a file:
- 輸入「-h」顯示操作說明:
bash-4.4$ ./md5.sh -h
Usage:
md5.sh
md5.sh FILE
md5.sh -h
- 輸入超過一個目標
bash-4.4$ ./md5.sh md5.sh video.mp4 video_mp4_md5.txt
Error: too many arguments: 3.
- 輸入目標不是檔案:
bash-4.4$ ./md5.sh error_file
Error: 'error_file' is not a file.
- 輸入目標是腳本:
bash-4.4$ ./md5.sh md5.sh
Error: 'md5.sh' is... myself!
- 輸入目標已是 MD5 檔案:
bash-4.4$ ./md5.sh video_mp4_md5.txt
Error: 'video_mp4_md5.txt' is a frame MD5 checksum file.
- 輸入目標不是可接受的影音檔:
bash-4.4$ ./md5.sh video_mp4.json
Error: 'video_mp4.json' is not an AV file.
- 輸入一個可接受的影音檔:
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 判斷式來讓流程更簡明。
此文所使用的腳本與影片可由下列連結取得:
# 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」,表有錯誤的中止腳本。
為了避免久未使用而忘記,或腳本會提供給其他使用者,通常會在腳本中提示基本的操作方式或說明文件,通常會透過 -h 或 -help 的參數來做為顯示說明的方式。因此建立一個名為 prompt_help 的函式,這個函式的工作是顯示操作說明。
function prompt_help() {
script_name="$(basename "${0}")"
# 定義變數 script_name 為腳本名稱
# 使用 basename 去除
# 定義變數 script_name 為腳本名稱
# 使用 basename 去除
cat <<EOF
# 使用 cat 指令來顯示下列字串,直到 EOF 為止
# 由此開始到 EOF 前為顯示的資訊
# 使用 cat 指令來顯示下列字串,直到 EOF 為止
# 由此開始到 EOF 前為顯示的資訊
Usage:
${script_name}
${script_name} FILE
${script_name} -h
EOF
exit 0
# 由於顯示說明是這個腳本正常的結束狀態,因此以 exit 0 結尾
# 由於顯示說明是這個腳本正常的結束狀態,因此以 exit 0 結尾
}
腳本的主要指令區段從讀取目標開始,首先使用一個 if 判斷式來將目標讀入變數 input_file 中。如果輸入為 "-h" 時,則顯示說明文件。
# Main section ########
# read input
# 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 這個函式
與前文所寫的 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 檔
# 若輸入目標中有 "#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
在確定輸入目標為符合預期的影音檔後,便可以執行想要的主要指令:
# generate frame MD5 checksums
filename="${input_file%.*}_${input_file##*.}_md5.txt"
ffmpeg -i "${input_file}" -f framemd5 "${filename}"
實際執行這個腳本試試:
- 沒有輸入目標:bash-4.4$ ./md5.shPlease drag-and-drop a file:
- 輸入「-h」顯示操作說明:bash-4.4$ ./md5.sh -hUsage:md5.shmd5.sh FILEmd5.sh -h
- 輸入超過一個目標bash-4.4$ ./md5.sh md5.sh video.mp4 video_mp4_md5.txtError: too many arguments: 3.
- 輸入目標不是檔案:bash-4.4$ ./md5.sh error_fileError: 'error_file' is not a file.
- 輸入目標是腳本:bash-4.4$ ./md5.sh md5.shError: 'md5.sh' is... myself!
- 輸入目標已是 MD5 檔案:bash-4.4$ ./md5.sh video_mp4_md5.txtError: 'video_mp4_md5.txt' is a frame MD5 checksum file.
- 輸入目標不是可接受的影音檔:bash-4.4$ ./md5.sh video_mp4.jsonError: 'video_mp4.json' is not an AV file.
- 輸入一個可接受的影音檔: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, 308cbed68d0bc873c9dabd0bb533af010, 118, 118, 1, 115200, a26e0120565fef2dcfd024c310b858d50, 119, 119, 1, 115200, d019222dd7a29a2d810b863aa61e6d5f
透過函式(function)來建構腳本,可以讓腳本的結構和流程變得很清楚,讓日後的維護也更方便。下篇會回到判斷式,當已經預知會有某些狀況時,並不一定要使用 if 判斷式去逐一判斷,下篇會講解如何使用 case 判斷式來讓流程更簡明。
此文所使用的腳本與影片可由下列連結取得:
留言
張貼留言