我有以下情况:
我想浏览目录$1
中的文件,直到其中一个符合我的状况为止。
详细:我想测试目录是否包含音频文件。找到第一个音频文件后,应进行process_audio_dir
;如果DIR中没有音频文件,process_noaudio
将发生。
到目前为止我的解决方案:
if [[ -z $(file -b "$1"/* | grep -i audio) ]]; then
echo "there are no audio files"; process_noaudio
else
echo "at least one audio file"; process_audio_dir
fi
file -b
告诉我文件是什么。
我从用set -x
查看它的猜测是,这将在所有文件上运行 file -b
,将结果放入一行,并抓住该行以进行匹配。(也许这是一个错误的假设)。
我宁愿有一个循环,直到找到第一个音频文件(一个足以匹配条件)并在那里停止/断开,或者如果没有音频文件,则继续使用process_noaudio
。
我感觉到/直到要实现这一目标,但我无法弄清楚。
将是什么(您的首选|最佳实践|最优雅|最低成本|最快|最快的)方法,可以在第一次匹配之前检查目录中的每个文件?
最安全的方法可能是直接迭代全球结果,因此您不容易受到包含特殊字符的文件名:
for path in "$1"/*; do
if file -b -- "$path" | grep -qi audio; then
printf 'Found an audio file %sn' "$path"
process_audio_dir
exit
fi
done
# since we didn't exit above, most be no audio files
printf "Didn't find any audio filesn"
process_noaudio
替代,如果您不想退出那里,则可以设置一个标志,指示您找到它并在循环后检查该标志,然后在if
内使用break
,一旦找到一个循环,就可以退出循环。
您的原因是将grep
应用于所有file
结果的输出,是因为Glob首先扩展,然后您运行命令,例如
file -b dir/file1 dir/file2 dir/file3 ...
然后,该命令的输出将被馈送到grep
我的解决方案将球放在命令的"外部"上,因此我们将单独运行每个文件。当然,在很多次启动file
时,还有更多的开销,因此对我来说并不是很明显的,这将更加有效。这可能取决于有多少文件,第一个音频文件通常在列表中的距离以及类似的内容。
正如评论中提到的那样,在find
或ls
的印刷文件名结果上迭代是危险的,因为这些结果将受到单词分割的约束,并有可能取决于您的确切操作。使用上面的for
循环是建议这样做的推荐方法。有关更多信息,请参见不要解析
效率低下,但兼容
find "$1" -maxdepth 1 -type f
-exec sh -c 'file -b -- "$1" | grep -qi audio' _ {} ;
-print
-quit
在这里,我们正在执行每个单独名称上运行file
的Shell管道,然后调用grep
检查其结果。这显然效率低下,但是由于-exec
运行的Shell命令返回非零退出代码时,find
仍将在第一个结果中提早退出,而grep
将返回真相值(因此允许-print
和-quit
操作运行)。
有效,但仅GNU-
shopt -s nocaseglob # enable case-insensitive matching
while IFS= read -r -d '' filename && IFS= read -r type; do
if [[ $type = *audio* ]]; then
break # exit the loop with the name in "$filename" and the type in "$type"
fi
done < <(find "$1" -type f -maxdepth 1 -exec file -b -0 -- '{}' +)
echo "Found file $filename of type $type"
在这里,我们正在运行尽可能少的file
实例(使用-exec ... {} +
将多个文件名传递给每个file
调用),使用GNU扩展-0
在每个文件名中输出后打印一个NUL。然后使用一对read
命令对此输出进行解析,并检查了子字符串audio
。
在posix c api中,我们拥有opendir()/readdir(),这是将目录递增为流的最灵活的方法。
没有这些功能的映射。 ls
是您在bash中列出目录内容的主要方式。您可以使用ls
进行以下操作以将列表处理为流(但是我很确定ls
是否会缓冲列表。ls
确实支持对可以进行缓冲的排序):
ls "$1" | while read f; do test -f $f && echo $f | grep -q "audio" && echo $f && break; done
另一个常见工具是find
。逐步查找作品。以下内容适用于您的用例,即"迭代目录内容直到条件匹配",并且比ls
更好。这只是打印第一个找到的文件名。调整条件匹配时您想做什么:
find -maxdepth 1 -type f -exec "bash" -c "file -b '{}' | grep -qi audio" ";" -print -quit