!["while 루프"를 사용한 후 bash 스크립트에서 데이터를 처리하는 데 문제가 있습니다.](https://linux55.com/image/198867/%22while%20%EB%A3%A8%ED%94%84%22%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%9C%20%ED%9B%84%20bash%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C%20%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC%20%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%8D%B0%20%EB%AC%B8%EC%A0%9C%EA%B0%80%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4..png)
ffprobe
비디오 폴더를 읽고 처리할 비디오 파일 목록을 생성하여 코덱을 식별하는 스크립트를 생성하려고 합니다 . 특정 코덱(이 경우 HEVC)으로 처리되지 않은 동영상은 추가 처리를 위해 새 목록에 추가되어야 합니다 ffmpeg
.
ffprobe_input
매우 기본적인 스크립트를 만들었지만 다음 입력으로 전달될 변수를 변경해야 하는 문제에 봉착했습니다 ffprobe
.
또한 스크립트의 이 부분이 제대로 작동하더라도 처리 후에 필터링된 파일 목록을 생성하는 방법에 대해 혼란스럽습니다. ffprobe
유일한 출력은 hevc
다음과 같은 단어이기 때문입니다 x264
.
아래에는 좀 더 설명이 풍부한 내 메모와 내가 작업을 수행하기 위해 시도한 몇 가지 방법과 함께 실제 스크립트가 있습니다.
이는 스크립트의 의도된 용도입니다../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
답변1
파일 이름에 개행 문자가 포함되어 있지 않다고 가정하면 어떤 식으로든 파일 이름을 손상시킬 필요가 없습니다. 출력은 filename 당 한 줄 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
다른 일도 많이 처리하고 괄호 등으로 토해낸다.
find
이미 포함된 항목 에 출력을 추가할지 full_list.txt
, 아니면 목록을 다시 생성할지 확실하지 않습니다. 목록은 즉시 처리되므로 오래된 콘텐츠는 무시하는 것이 더 합리적이라고 생각합니다.
terdon 주석과 마찬가지로 파일 이름 목록을 저장하기 위해 중간 파일이 꼭 필요한 것은 아닙니다. find ... | while IFS= read file, do ...
Bash/ksh/zsh에서 대체를 실행하거나 처리할 수 있습니다 while IFS= read file, do ... done < <(find ...)
. while 루프 내부에 변수를 설정하려면 둘 사이의 차이점이 중요합니다. 다음을 참조하세요.내 변수가 하나의 "읽는 동안" 루프에서는 로컬이지만 겉보기에 유사한 다른 루프에서는 로컬이 아닌 이유는 무엇입니까?