在 bash 脚本中,我想做一个典型的“for file in somedir”,但我希望按照“ls -v”返回文件的相同顺序处理文件。我知道使用“ls”作为函数的缺点。有没有办法在不使用“ls”的情况下复制“-v”?谢谢。
最佳答案
假设这是“版本号”排序顺序,这也是 GNU 实现的 sort
。因此,在 GNU 平台上:
somedir=/foo
while IFS= read -r -d '' filename; do
printf 'Processing file: %q\n' "$filename"
done < <(set -- "$somedir"/*; [[ -e $1 || -L $1 ]] && printf '%s\0' "$@" | sort -z -V)
如果您真的想要使用for
循环而不是while
循环,解析为数组并迭代它:
files=( )
while IFS= read -r -d '' filename; do
files+=( "$filename" )
done < <(set -- "$somedir"/*; [[ -e $1 || -L $1 ]] && printf '%s\0' "$@" | sort -z -V)
for filename in "${files[@]}"; do
printf 'Processing file: %q\n' "$filename"
done
解释上面的一些魔法:
- 在
< <(...)
,<(...)
是流程替换。它被替换为一个文件名,当读取该文件名时,将返回所包含代码的输出。因此,< <(...)
将该进程替换的输出作为while read
的输入环形。此循环形式在BashFAQ #1中描述。 。 BashFAQ #24 中给出了使用这种重定向而不是通过管道进入循环的原因。 . -
set -- "$somedir"/*
用"$somedir"/*
的结果替换当前上下文中的参数列表(该上下文是运行进程替换的子 shell!) ;因此,(默认情况下,非隐藏)变量somedir
中指定的目录的内容. -
[[ -e $1 || -L $1 ]]
仅当该 glob 扩展到至少一项时才为 true;如果仍然存在*
(并且不存在具有该名称的实际文件系统对象),在这种情况下控制输出可以防止进程替换发出任何输出。 -
sort -z
告诉sort
使用 NUL 来分隔输入和输出中的元素 - NUL 是不允许存在于文件名中的字符。
关于bash for 循环的顺序与 GNU "ls -v"("version-number"排序相同),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47169365/