我有一个看起来像这样的文件结构
./501.res/1.bin
./503.res/1.bin
./503.res/2.bin
./504.res/1.bin
我想.bin
在每个目录中找到文件名最大的文件的文件路径。所以我正在寻找的输出将是
./501.res/1.bin
./503.res/2.bin
./504.res/1.bin
一个文件可以有的最大数字是 9。
问题
我如何在 BASH 中做到这一点?
我已经到了find .|grep bin|sort
Glob 保证按词汇顺序扩展。
for dir in ./*/
do
files=($dir/*) # create an array
echo "${files[@]: -1}" # access its last member
done
测试:
find . -type d -name '*.res' | while read dir; do
find "$dir" -maxdepth 1 | sort -n | tail -n 1
done
怎么用awk
?你可以很简单地得到第一次出现:
[ghoti@pc ~]$ cat data1
./501.res/1.bin
./503.res/1.bin
./503.res/2.bin
./504.res/1.bin
[ghoti@pc ~]$ awk 'BEGIN{FS="."} a[$2] {next} {a[$2]=1} 1' data1
./501.res/1.bin
./503.res/1.bin
./504.res/1.bin
[ghoti@pc ~]$
要获得最后一次出现,您可以通过几种管道:
[ghoti@pc ~]$ sort -r data1 | awk 'BEGIN{FS="."} a[$2] {next} {a[$2]=1} 1' | sort
./501.res/1.bin
./503.res/2.bin
./504.res/1.bin
[ghoti@pc ~]$
鉴于您正在使用“find”和“grep”,您可能可以这样做:
find . -name \*.bin -type f -print | sort -r | awk 'BEGIN{FS="."} a[$2] {next} {a[$2]=1} 1' | sort
这是如何运作的?
该find
命令有许多有用的选项,包括通过 glob 选择文件、选择文件类型等的能力。它的输出你已经知道,它成为sort -r
.
首先,我们对输入数据进行反向排序(sort -r
)。这确保了在任何目录中,编号最高的文件将首先出现。该结果被输入 awk。FS 是字段分隔符,它$2
包含“/501”、“/502”等内容。Awk 脚本具有condition {action}
对每一行输入进行评估的部分。如果缺少某个条件,则该操作将在每一行上运行。如果条件为“1”且没有操作,则打印该行。所以这个脚本分解如下:
a[$2] {next}
- 如果存在下标$2(即“/501”)的数组a
,则直接跳到下一行。否则...{a[$2]=1}
- 将数组下标 $2 设置为 1,以便将来第一个条件评估为真,然后...1
- 打印该行。此 awk 脚本的输出将是您想要的数据,但顺序相反。最后sort
将事情按您期望的顺序放回原处。
现在......这是很多管道,当您要求它同时处理数百万行输入时,排序可能会有点资源消耗。这个解决方案对于少量文件来说已经足够了,但是如果你要处理大量的输入,请告诉我们,我可以提出一个一体化的 awk 解决方案(这将需要超过 60 秒来写)。
更新
根据 Dennis 的明智建议,我上面包含的 awk 脚本可以通过将其从
BEGIN{FS="."} a[$2] {next} {a[$2]=1} 1
至
BEGIN{FS="."} $2 in a {next} {a[$2]} 1
虽然这在功能上是相同的,但优点是您只需定义数组成员而不是为它们分配值,这可能会节省内存或 cpu,具体取决于您的 awk 实现。无论如何,它更干净。
我想出了这样的东西:
for dir in $(find . -mindepth 1 -type d | sort); do
file=$(ls "$dir" | sort | tail -n 1);
[ -n "$file" ] && (echo "$dir/$file");
done
也许它可以更简单
如果从 find 中调用 shell 是一个选项,试试这个
find * -type d -exec sh -c "echo -n './'; ls -1 {}/*.bin | sort -n -r | head -n 1" \;
这是一个班轮
find . -mindepth 1 -type d | sort | sed -e "s/.*/ls & | sort | tail -n 1 | xargs -I{} echo &\/{}/" | bash