이 스크립트에서 "ls"의 출력을 안정적으로 캡처하려면 어떻게 해야 합니까?

이 스크립트에서 "ls"의 출력을 안정적으로 캡처하려면 어떻게 해야 합니까?

다음 스크립트는 play_movie.sh자동으로 영화(현재 디렉터리에 영화가 하나만 있는 경우)를 선택하고 사용하도록 설계되었습니다 ffplay. 그렇지 않으면 사용자에게 일련의 영화를 표시하고 사용자의 입력(재생할 영화)을 받아들이도록 설계되었습니다.

#!/usr/bin/bash

output_a=$(ls -R *.[aA][vV][iI] 2>/dev/null)
output_m=$(ls -R *.[mM][kKpP][vV4]] 2>/dev/null)
output_v=$(ls -R *.[vV][oO][bB]] 2>/dev/null)
all_exts="${output_a}${output_m}${output_v}"
ln_cnt=$(echo "${all_exts}" | wc -l)

if [[ "$ln_cnt" -eq 1 ]]; then
  echo -e "Playing: ${all_exts}\n"
  ffplay -hide_banner -infbuf -fs -sn -ast a:0 "${all_exts}"
elif [[ "$ln_cnt" -gt 1 ]]; then
  printf "Select a file out of the list below"
  ls -R *.[aA][vV][iI] 2>/dev/null 
  ls -R *.[mM][kKpP][vV4] 2>/dev/null 
  ls -R *.[vV][oO][bB] 2>/dev/null
  read line; ffplay -hide_banner -infbuf -fs -sn -ast a:0 "$line"
fi

이 스크립트에 대한 내 질문은 다음과 같습니다.

  1. if현재 디렉터리에 많은 영화가 있더라도 스크립트는 문의 첫 번째 분기를 실행합니다. 그러면 나는 이해했다 No such file or directory.
  2. 상영할 영화가 단 한 편뿐이라면 나는 No such file or directory

ls과거의 결과 에 의존하는 데 어려움을 겪습니다 . 이 스크립트를 사용하지 않고 수정할 수 있습니까 find?

답변1

"현재 디렉터리에 하나만 있는 경우" 부분이 정확하다고 가정합니다(현재 디렉터리에 있는 파일에만 관심이 있습니다. ls다음은 물론 을 사용하지 않아도 됩니다 find.

#!/usr/bin/bash -

shopt -s nullglob
set -- *.[aA][vV][iI] *.[mM][kKpP][vV4] *.[vV][oO][bB]

if [[ "$#" -eq 1 ]]; then
  printf>&2 "Playing: %s\n\n" "$@"
  ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- "$@"
elif [[ "$#" -gt 1 ]]; then
  printf>&2 "Select a file out of the list below:\n"
  printf>&2 "%s\n" "$@"
  IFS= read -r line &&
    ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- "$line"
fi

이 설정은 nullglob일치하지 않는 glob을 제거한 다음 매개변수를 일치하는 모든 파일로 설정합니다.

그림에서 알 수 있듯이Stefan Chazeras의 답변select, 사용자 경험을 개선하는 데 사용할 수 있습니다 ( select위치 매개변수는 기본적으로 사용되며 위의 사용법과 잘 맞습니다 set).

#!/usr/bin/bash -

shopt -s nullglob
set -- *.[aA][vV][iI] *.[mM][kKpP][vV4] *.[vV][oO][bB]

if [[ "$#" -eq 1 ]]; then
  printf>&2 "Playing: %s\n\n" "$@"
  ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- "$@"
elif [[ "$#" -gt 1 ]]; then
  select video; do
    ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- "$video"
    exit
  done
fi

답변2

이 방법으로는 사용할 수 없습니다 ls. 이것들은 *.[aA][vV][iI]쉘에 의해 확장되며 대신에 lsfor -R는 디렉터리의 내용을 재귀적으로 나열 ls하게 합니다 R. 그렇게 하려면 파일이 아닌 디렉터리의 경로를 전달해야 하며 그런 다음에도 파일 목록을 출력합니다( 숨겨지지 않은 파일)에는 해당 디렉터리 구성 요소가 이러한 디렉터리에 없으므로 ffmpeg찾을 수 없습니다.

--zero또한 최신 버전의 GNU 옵션 구현을 사용하지 않으면 ls출력을 ls전혀 안정적으로 후처리할 수 없습니다.

현재 작업 디렉토리와 그 아래 어디에서나 해당 확장자 목록을 가진 파일을 찾으려면 를 사용할 수 있습니다 find. 파일 목록(또는 해당 항목)을 저장하려면 문자열/스칼라 변수 대신 배열 변수를 사용할 수 있습니다.

zshzsh에는 (일반적으로 작동하는) 재귀 globbing 및 glob 한정자가 있으므로 여기서 대신 사용하면 bash더 쉬워집니다. 즉, 파일 목록을 해당 유형으로 제한할 수도 있습니다.정기적인. Glob은 기본적으로 숨겨진 파일도 건너뜁니다(비슷 ls하지만 find수동으로 건너뛸 필요는 없음).

#! /usr/bin/zsh -
set -o extendedglob
videos=( **/*.(#i)(avi|mkv|mp4|vob)(N-.) )

play() {
  print -ru2 Playing $argv
  ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- $argv
}

case $#videos in
  (0) print -u2 No video found; exit 1;;
  (1) play $videos;;
  (*) print -u2 Select a video:
      select video in $videos; do
        play $video || exit
        break
      done;;
esac

bash(GNU 쉘) + GNU를 사용하면 find다음을 수행할 수 있습니다.

#! /usr/bin/bash -
readarray -td '' videos < <(
  LC_ALL=C find . \
    -regextype posix-extended \
    -name '.?*' -prune -o \
      -iregex '.*\.(avi|mkv|mp4|vob)' \
      -xtype f \
      -printf '%P\0' |
    sort -z
)

shopt -u xpg_echo
play() {
  echo>&2 -E Playing "$@"
  ffplay -hide_banner -infbuf -fs -sn -ast a:0 -- "$@"
}

case ${#videos[@]} in
  (0) echo>&2 No video found; exit 1;;
  (1) play "${videos[@]}";;
  (*) echo>&2 Select a video:
      select video in "${videos[@]}"; do
        play "$video" || exit
        break
      done;;
esac

기타 참고사항:

  • echo -e파일 이름에 문자가 포함되어 있으면 중단됩니다 \. 여기 있어요아니요e스케이프 시퀀스를 확장 하려면 -Efor echo또는 -rfor 를 사용하세요 print. 추가 줄바꿈을 인쇄하려면 echoliteral 또는 with 의 마지막 매개변수 에 추가하세요 $'\n'. 또한 옵션은 설정 여부에 관계없이 허용됩니다 bash(일부 시스템 및/또는 일부 환경에서는 기본적으로 설정됨). 일반적으로 피하는 것이 가장 좋지만 공백으로 연결된 인수를 쉽게 인쇄할 수 있기 때문에 여기서는 여전히 사용합니다(사용하기가 더 번거롭고 bash는 로드 가능한 플러그인으로만 존재하며 시스템에는 기본적으로 bash에 포함되어 있습니다). 쉘이 있다는 것을 알고 있으므로 이 기능을 사용하여 한 번에 여러 비디오를 재생할 수 있습니다.echoposixxpg_echoechoprintfprintbashplay
  • 프롬프트와 사용자 메시지는 일반적으로 stderr(fd 2)로 전송되어야 합니다. select예를 들어, 이것이 의 출력이 가는 곳입니다.
  • 한 줄을 읽는 구문은 다음과 같습니다.IFS= read -r line, 아니요 read line. 그러나 어쨌든 파일 경로는 한 줄로 구성될 필요가 없습니다. 개행 문자는 파일 이름의 모든 문자만큼 유효하기 때문입니다.
  • 당신 *.[mM][kKpP][vV4]도 일치할 것 files.mv4입니다.m4v).

관련 정보