以下脚本旨在修剪当前工作目录中的所有媒体文件。
#!/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
infoo
从终端读取某些内容,但是,它是在标准输入流从某个文件重定向的上下文中调用的。
这意味着read
infoo
将从输入文件的输入流中读取,而不是从终端读取。
您可以通过使循环从标准输入之外的另一个文件描述符读取来规避此问题:
foo () {
read variable
}
while read something <&3; do
foo
done 3<input-file
这里,read
循环中从文件描述符 3 读取,该描述符在关键字之后连接到输入文件done
。这使得函数read
中的foo
可以自由地使用原始标准输入流。
在bash
shell 中,您可以让 shell 在 shell 变量中分配描述符,而不是对额外的文件描述符使用硬编码值:
foo () {
read variable
}
while read something <&"$fd"; do
foo
done {fd}<input-file
这可能会设置$fd
为 10 或更大的整数。确切的值并不重要。
在问题中当前的代码中,您还可以通过避免创建和读取文件列表来解决问题,而是直接使用文件 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
是必要的,因为默认情况下,如果该模式没有匹配的名称,shell 将保留通配模式。您可以通过设置来删除shellif
中的语句bash
nullglob
shell 选项使用 shopt -s nullglob
。设置此选项将使bash
shell 完全删除不匹配的 glob。
另请注意,如果与通配模式匹配的任何名称是一个,则这与您的代码中的不同目录。如果您有一个名为 eg 的目录mydir.mp3
,那么ls
会列出内容该目录的。此外,如果与模式匹配的文件名以破折号开头,则使用的代码ls
可能会将该名称误认为是一组选项。