以下脚本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
我对这个脚本的问题是:
if
即使当前目录中有很多电影,脚本也会执行语句的第一个分支。然后我明白了No such file or directory
。- 如果只有一部电影要播放,我会的
No such file or directory
。
我很难依赖ls
过去的输出。是否可以修复此脚本而无需使用find
?
假设“如果当前目录中只有一个”部分是正确的(即您只关心当前目录中的文件),您可以避免使用
ls
,更不用说find
:此设置
nullglob
将删除不匹配的 glob,然后将参数设置为所有匹配的文件。正如Stéphane Chazelas 的回答所示,您可以使用它
select
来改善用户体验(select
默认使用位置参数,这与set
上面的使用非常吻合):你不能
ls
这样使用。这些*.[aA][vV][iI]
是由 shell 扩展的,而不是ls
,-R
forls
是让它R
递归地列出目录的内容,为此你需要向它传递目录的路径,而不是文件,即使这样,它也会输出文件列表(任何非隐藏文件)在这些目录中没有其目录组件,因此ffmpeg
无法找到它们。另外,除非您使用
--zero
GNU 实现的最新版本的选项ls
,否则 的输出ls
根本无法可靠地进行后处理。要在当前工作目录及以下任意位置查找具有该扩展名列表的文件,您可以使用
find
. 要存储文件列表(或任何相关的内容),您可以使用数组变量,而不是字符串/标量变量。使用
zsh
而不是在bash
这里会让它变得更容易,因为 zsh 具有(正常工作)递归 globbing 和 glob 限定符,这意味着您还可以将文件列表限制为常规类型的文件列表。默认情况下,Glob 也会跳过隐藏文件(类似于ls
但不是find
您需要手动跳过的地方):使用
bash
(GNU shell) + GNUfind
,您可以执行以下操作:其他一些注意事项:
echo -e
如果文件名包含字符,则会中断\
。在这里,您不希望扩展e
scape 序列,因此需要使用-E
forecho
或-r
forprint
。如果您希望它打印额外的换行符,请将其添加到echo
literal 或 with的最后一个参数中$'\n'
。另请注意,仅当 或未设置时才bash
接受选项(并且它们在某些系统和/或某些环境中默认设置)。一般来说,最好避免使用,我仍然在这里使用它,因为它能够轻松地打印与空格连接的参数(使用 时更麻烦,并且 bash 只有echo
posix
xpg_echo
echo
printf
print
作为一个可加载插件,很少有系统在有 bash 时默认包含)并且基于我们知道 shell 是bash
,因此该play
功能可用于一次播放多个视频。select
例如,这就是 's 输出的位置。IFS= read -r line
, notread line
。但无论如何,文件路径不必由一行组成,因为换行符与文件名中的任何字符一样有效。*.[mM][kKpP][vV4]
也会匹配files.mv4
(notm4v
)。