왜 이 함수의 실행이 while 루프에서 벗어나나요?

왜 이 함수의 실행이 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화면에 인쇄됩니다.

내 코드에 문제가 있나요?

답변1

코드는 기본적으로 다음을 수행합니다.

foo () {
    read variable
}

while read something; do
    foo
done <input-file

목적은 터미널에서 무언가를 read읽는 foo것이지만 일부 파일에서 리디렉션되는 표준 입력 스트림의 컨텍스트에서 호출됩니다.

read이는 in foo이 터미널이 아닌 입력 파일의 입력 스트림에서 읽는다는 것을 의미합니다 .

표준 입력이 아닌 다른 파일 설명자에서 루프를 읽도록 하면 이 문제를 피할 수 있습니다.

foo () {
    read variable
}

while read something <&3; do
    foo
done 3<input-file

여기서 read루프는 키워드 다음에 입력 파일에 연결된 파일 설명자 3에서 읽습니다 done. 이를 통해 함수 read내의 함수가 foo원시 표준 입력 스트림을 자유롭게 사용할 수 있습니다.

bash셸 에서는 추가 파일 설명자에 대해 하드 코딩된 값을 사용하는 대신 셸 변수에 설명자를 할당하도록 할 수 있습니다.

foo () {
    read variable
}

while read something <&"$fd"; do
    foo
done {fd}<input-file

$fd10 이상의 정수로 설정할 수 있습니다 . 정확한 값은 중요하지 않습니다.


문제의 현재 코드에서는 파일 목록을 생성하고 읽는 것을 피하고 대신 파일 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패턴과 일치하는 이름이 없는 경우 기본적으로 쉘이 와일드카드 패턴을 유지하기 때문에 필요합니다. 다음을 설정하여 셸 if에서 명령문을 제거할 수 있습니다.bashnullglob 쉘 옵션 사용 shopt -s nullglob. 이 옵션을 설정하면 bash셸이 일치하지 않는 글로브를 완전히 제거합니다.

또한 와일드카드 패턴과 일치하는 이름이목차. 예를 들어 디렉토리가 있으면 나열됩니다 mydir.mp3.ls콘텐츠이 디렉토리의. 또한, 패턴과 일치하는 파일 이름이 대시로 시작하는 경우 사용된 코드가 ls옵션 세트의 이름을 착각할 수 있습니다.

관련 정보