AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 754645
Accepted
John Smith
John Smith
Asked: 2023-08-22 16:20:17 +0800 CST2023-08-22 16:20:17 +0800 CST 2023-08-22 16:20:17 +0800 CST

如何可靠地捕获此脚本中“ls”的输出?

  • 772

以下脚本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?

ls
  • 2 2 个回答
  • 401 Views

2 个回答

  • Voted
  1. Best Answer
    Stephen Kitt
    2023-08-22T16:49:46+08:002023-08-22T16:49:46+08:00

    假设“如果当前目录中只有一个”部分是正确的(即您只关心当前目录中的文件),您可以避免使用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,然后将参数设置为所有匹配的文件。

    正如Stéphane Chazelas 的回答所示,您可以使用它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
    
    • 10
  2. Stéphane Chazelas
    2023-08-22T16:54:46+08:002023-08-22T16:54:46+08:00

    你不能ls这样使用。这些*.[aA][vV][iI]是由 shell 扩展的,而不是ls,-Rforls是让它R递归地列出目录的内容,为此你需要向它传递目录的路径,而不是文件,即使这样,它也会输出文件列表(任何非隐藏文件)在这些目录中没有其目录组件,因此ffmpeg无法找到它们。

    另外,除非您使用--zeroGNU 实现的最新版本的选项ls,否则 的输出ls根本无法可靠地进行后处理。

    要在当前工作目录及以下任意位置查找具有该扩展名列表的文件,您可以使用find. 要存储文件列表(或任何相关的内容),您可以使用数组变量,而不是字符串/标量变量。

    使用zsh而不是在bash这里会让它变得更容易,因为 zsh 具有(正常工作)递归 globbing 和 glob 限定符,这意味着您还可以将文件列表限制为常规类型的文件列表。默认情况下,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 shell) + 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如果文件名包含字符,则会中断\。在这里,您不希望扩展escape 序列,因此需要使用-Eforecho或-rfor print。如果您希望它打印额外的换行符,请将其添加到echoliteral 或 with的最后一个参数中$'\n'。另请注意,仅当 或未设置时才bash接受选项(并且它们在某些系统和/或某些环境中默认设置)。一般来说,最好避免使用,我仍然在这里使用它,因为它能够轻松地打印与空格连接的参数(使用 时更麻烦,并且 bash 只有echoposixxpg_echoechoprintfprint作为一个可加载插件,很少有系统在有 bash 时默认包含)并且基于我们知道 shell 是bash,因此该play功能可用于一次播放多个视频。
    • 提示和用户消息通常应发送至 stderr (fd 2)。select例如,这就是 's 输出的位置。
    • 读取一行的语法是IFS= read -r line, not read line。但无论如何,文件路径不必由一行组成,因为换行符与文件名中的任何字符一样有效。
    • 请注意,您*.[mM][kKpP][vV4]也会匹配files.mv4(not m4v)。
    • 8

相关问题

  • 检查 Ubuntu 中的磁盘空间(使用 ls)

  • zsh:“ls -S”在实用程序文件夹中不起作用

  • 使用“ls”时如何将文件日期转换为特定格式

  • 管道 ls 是否与 ls -1 相同?

  • 如何使用 ls 的输出打开最后修改的文件?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve