為什麼這些函式的執行會跳出這個while循環?
以下腳本旨在修剪目前工作目錄中的所有媒體文件。
#!/usr/bin/bash trimmer() { of=$(echo "${if}"|sed -E "s/(\.)([avimp4kvweb]{3,3}$)/\1trimmed\.\2/") ffmpeg -hide_banner -loglevel warning -ss "${b}" -to "${ddecreased}" -i "${if}" -c copy "${of}" echo "Success. Exiting .." } get_ddecreased() { duration="$(ffprobe -v quiet -show_entries format=duration -hide_banner "${if}"|grep duration|sed -E s/duration=\([0-9]*\)\..*$/\\1/)" echo ${duration} ddecreased="$(echo "${duration} - ${trimming}"|bc -l)" echo ${ddecreased} } rm_source() { echo -e "Remove ${if}?[Y/n]?" read ch if [[ "${ch}" == 'y' ]]; then rm "${if}" fi } echo "How much of the beginning would you like to trim?" read b echo "How much of the end would you like to trim?" read trimming ls *.avi *.mkv *.mp4 *.vob >list_of_files echo "Prompt before removing the source[Y/n]?" read ch while IFS="" read -r if || [[ -n "${if}" ]]; do if [[ "${ch}" == 'y' ]]; then get_ddecreased && trimmer && rm_source elif [[ "${ch}" == 'n' ]]; then get_ddecreased && trimmer && rm "${if}" fi echo $if done <list_of_files echo -e "Removing list_of_files." rm list_of_files
如果使用者
y
在詢問時選擇Prompt before removing the source[Y/n]
並trimmer
完成了第一個文件的修剪,rm_source
則意味著提示使用者並在刪除源文件之前**等待他們的輸入。**這不起作用,因為腳本不等待輸入並立即進行,echo -e "Removing list_of_files."
就像根本沒有 while 循環一樣。n
當使用者在被詢問時選擇時,while 循環也不會執行Prompt before removing the source[Y/n]
- 腳本直接繼續執行,echo -e "Removing list_of_files."
而不是遍歷list_of_files
. 為什麼這樣?然而,當我註釋掉所有這些行時if [[ "${ch}" == 'y' ]]; then get_ddecreased && trimmer && rm_source elif [[ "${ch}" == 'n' ]]; then get_ddecreased && trimmer && rm "${if}" fi
在 while 循環中,所有行都
list_of_files
列印到螢幕上。我的程式碼有什麼問題?
您的程式碼本質上是在執行以下操作:
foo () { read variable } while read something; do foo done <input-file
目的是讓
read
infoo
從終端讀取某些內容,但是,它是在標準輸入流從某個文件重定向的上下文中呼叫的。這意味著
read
infoo
將從來自輸入文件的輸入流中讀取,而不是從終端讀取。您可以通過從標準輸入之外的另一個文件描述符讀取循環來規避此問題:
foo () { read variable } while read something <&3; do foo done 3<input-file
在這裡,
read
循環中的 in 從文件描述符 3 中讀取,該文件描述符在done
關鍵字之後連接到輸入文件。這使得函式read
中的foo
可以自由使用原始標準輸入流。在
bash
shell 中,您可以讓 shell 在 shell 變數中分配描述符,而不是為額外的文件描述符使用硬編碼值:foo () { read variable } while read something <&"$fd"; do foo done {fd}<input-file
這可能會設置
$fd
為 10 或更高的整數。確切的值並不重要。在問題的目前程式碼中,您還可以通過避免創建和讀取文件列表來解決您的問題,而是直接使用文件 glob:
for filename in *.avi *.mkv *.mp4 *.vob; do if [ ! -e "$filename" ]; then # skip non-existing names continue fi # use "$filename" here # ... and call your rm_source function done
這完全避免了重定向。這也允許您的程式碼處理名稱中帶有換行符的奇數文件。
循環中的
if
語句用於測試命名文件是否存在,這是必要的,因為預設情況下,如果該模式沒有匹配的名稱,shell 將保留通配模式。您可以通過使用設置shell 選項來擺脫 shell 中的if
語句。設置此選項將使shell 完全刪除不匹配的 glob。bash``nullglob``shopt -s nullglob``bash
還要注意,如果與通配模式匹配的任何名稱是目錄,則這與您的程式碼中的不同。如果您有一個名為 eg 的目錄
mydir.mp3
,那麼ls
將列出該目錄的內容。此外,如果與模式匹配的文件名以破折號開頭,則使用的程式碼ls
可能會將該名稱誤認為是一組選項。