Bash

使用“while 循環”並隨後在 bash 腳本中處理數據的問題

  • November 25, 2021

我正在嘗試創建一個腳本,該腳本應該讀取一個影片文件夾並創建一個要處理的影片文件列表,ffprobe以辨識編解碼器。未使用特定編解碼器(在本例中為 HEVC)處理的影片應放入新列表中,以供ffmpeg.

我創建了一個非常基本的腳本,但是在ffprobe_input需要更改變數以便作為ffprobe.

此外,即使腳本的這部分工作正常,我對如何在ffprobe處理後創建過濾後的文件列表感到困惑,因為唯一的輸出是一個單詞,例如:hevcx264.

實際的腳本在下面,連同我的筆記,應該更具描述性,在筆記中也是我試圖讓事情發揮作用的一些方法。

這是腳本的預期用途:./script.sh -p /path\ to\ videos

#!/bin/bash

#Read path (-p) input and exit on error.
while getopts p: flag
   do
       case "${flag}" in
           p) vpath=${OPTARG};;
           *) echo "usage: $0 [-p]" >&2
           exit 1 ;;
       esac
   done

#Now we echo the path for neatness
   echo -e "Selected root video path: $vpath";

#Check if the path is valid. The path must be escaped. Cd into the folder and execute: printf "%q\n" "$(pwd)"
   [ -d "$vpath" ] && echo "Directory $vpath exists." || echo "Error: Directory $vpath does not exist. Tip: make sure the spaces are escaped in folder names, ex: ===video\ folder===."

#Prepare a list of video files with full escaped paths,ready for ffprobe/ffmpeg input.
   find "$vpath" -type f \( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" \) | sed 's/ /\\ /g' >> full_list.txt

#read the total number of lines from full_list.txt
   nrl_total="$(wc -l full_list.txt | grep -Eo "[0-9]{0,7}")"
   echo -e "There are a total of $nrl_total videos for further processing."
   
#read line number and pass to $ffprobe_input
#   nrl=($(seq 1 "$nrl_total"))
#   nrl={1..$nrl_total..1}
#   for $nlr in {1..$nrl_total..1}; do
#   nrl=({1..$nrl_total..1})
filename='full_list.txt'
nrl=1
while read line; do
   echo "$nrl"
   nrl=$((n+1))
#done < $filename   

#ffprobe_input="$(sed -n 1p full_list.txt)" Use line number in "p" attribute, ex: 1p.
#   ffprobe_input="$(sed -n 1p full_list.txt)"
   ffprobe_input="$(sed -n "$nrl"p full_list.txt)"
   
#Now pass the input to ffprobe to determine if the videos are HEVC or not. Output is single word, ex: hevc or x264.
   eval ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 -i "$ffprobe_input"

done < $filename    

rm full_list.txt

假設您的文件名不包含換行符,則無需以任何方式破壞它們。每個文件名的輸出file只有一行,因此只需將其儲存並循環遍歷生成的文件:

> non-hevc.txt        # clear the output list
find "$vpath" -type f \( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" \) \
> full_list.txt
while IFS= read -r file; do 
   result=$(ffprobe -v error -select_streams v:0 -show_entries \
            stream=codec_name -of default=noprint_wrappers=1:nokey=1 -i "$file")
   if [ "$result" != hevc ]; then
       echo "$file" >> non-hevc.txt
   fi
done < full_list.txt
rm -f full_list.txt

在這裡, 的輸出ffprobe被命令替換擷取$(...)並儲存到result,然後我們看一下。

我看不出有任何理由sed -n "$nrl"p在循環內部讀取文件名列表,因為read已經讀取了同一行。不過,我們確實需要IFS=並且-r不破壞輸入。

也沒有理由用反斜杠轉義任何空格,引用的擴展"$file"將變數的內容按原樣傳遞給命令。撤消轉義也很困難,當您使用 時eval,它也會處理許多其他內容,並且會在括號中出現問題。

不確定是否要將輸出附加findfull_list.txt已包含的內容,或重新創建列表。由於我們立即處理列表,因此在我看來,忽略任何舊內容更有意義。

請注意,與 terdon 註釋一樣,您並不嚴格需要中間文件來儲存文件名列表。你可以做 just find ... | while IFS= read file, do ...,或者在 Bash/ksh/zsh 中使用程序替換while IFS= read file, do ... done < <(find ...)。如果您想在 while 循環中設置變數,兩者之間的區別很重要,請參閱:為什麼我的變數在一個“while read”循環中是局部的,但在另一個看似相似的循環中卻沒有?

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