Debian

將“for file in”轉換為“find”,以便我的腳本可以遞歸應用

  • December 15, 2018

我有這樣的想法,即執行一個 bash 腳本來檢查一些條件,並使用ffmpeg它來將我目錄中的所有影片從任何格式轉換為.mkv,它工作得很好!

問題是,我不知道for file in循環不能遞歸工作(https://stackoverflow.com/questions/4638874/how-to-loop-through-a-directory-recursively

但我幾乎不理解“管道”,並期待看到一個例子並清除一些不確定性。

我想到了這種情況,我認為這對我理解有很大幫助。

假設我有這個 bash 腳本片段:

for file in *.mkv *avi *mp4 *flv *ogg *mov; do
target="${file%.*}.mkv"
   ffmpeg -i "$file" "$target" && rm -rf "$file"
done

它的作用是,對於目前目錄,搜尋任何*.mkv *avi *mp4 *flv *ogg *mov然後聲明輸出具有其副檔名,.mkv然後刪除原始文件,然後輸出應保存到原始影片所在的同一文件夾中。

  1. 如何將其轉換為遞歸執行?如果我使用find,在哪裡聲明變數$file?你應該在哪裡申報$target?都是find真的單行嗎?我真的需要將文件傳遞給一個變數$file,因為我仍然需要執行條件檢查。
  2. 並且,假設(1)成功,如何確保滿足“然後輸出應保存到原始影片所在的同一文件夾”的要求?

你有這個程式碼:

for file in *.mkv *avi *mp4 *flv *ogg *mov; do
target="${file%.*}.mkv"
    ffmpeg -i "$file" "$target" && rm -rf "$file"
done

在目前目錄中執行。要將其轉換為遞歸過程,您有幾個選擇。最簡單的(IMO)是find按照您的建議使用。for 的語法find非常“不像 UNIX”,但這裡的原則是每個參數都可以與 AND 或 OR 條件一起應用。在這裡,我們要說“如果 this-filename 匹配 OR that-filename 匹配 Then print-it ”。文件名模式被引用,因此 shell 無法獲取它們(請記住,shell 負責擴展所有未引用的模式,因此如果您有一個未引用的模式*.mp4並且您janeeyre.mp4在目前目錄中,shell 將替換*.mp4為匹配,並且find會看到-name janeeyre.mp4而不是您想要的-name *.mp4;如果*.mp4匹配多個名稱…)。括號加上前綴\也可以防止外殼嘗試將它們作為子外殼標記(如果願意,我們可以引用括號來代替:)'('

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' \) -print

它的輸出需要輸入到while依次處理每個文件的循環的輸入中:

while IFS= read file    ## IFS= prevents "read" stripping whitespace
do
   target="${file%.*}.mkv"
   ffmpeg -i "$file" "$target" && rm -rf "$file"
done

現在剩下的就是用管道將這兩個部分連接在一起,|以便輸出find成為while循環的輸入。

當您測試此程式碼時,我建議您在兩者ffmpegrmwith前加上前綴,echo這樣您就可以看到執行的內容 - 以及使用哪些路徑。

這是最終結果,包括echo我推薦用於測試的語句:

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' \) -print |
   while IFS= read file    ## IFS= prevents "read" stripping whitespace
       do
           target="${file%.*}.mkv"
           echo ffmpeg -i "$file" "$target" && echo rm -rf "$file"
       done

使用 POSIX 查找:

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o \
         -name '*ogg' -o -name '*mov' \) -exec sh -c '
 for file do
   target="${file%.*}.mkv"
   echo ffmpeg -i "$file" "$target"
 done' sh {} +

替換echo為您要使用的任何命令。

如果你有 GNU find 或 BSD find,你可以使用-regex

find . -regex '.*\.\(mkv\|avi\|mp4\|flv\|ogg\|mov\)'

引用自:https://unix.stackexchange.com/questions/196633