我有一堆文件,全部命名如下:
name_file-1.txt
name_file-2.txt
name_file-3.txt
some_other_file-1.txt
some_other_file-2.txt
有数千种不同的文件名,有些-1.txt
最后只有一个,有些带有-1.txt
, -2.txt
...-60.txt
我需要复制每个文件的最大编号,因此name_file-3.txt
, some_other_file-2.txt
。如何在 Linux 命令行上执行此操作?
答案1
和zsh
:
typeset -A greatest
for f (*-*(n)) greatest[${f%-*}]=$f
cp -- $greatest /destination
*-*(n)
:名称包含-
(*-*
) 的非隐藏文件,按数字排序((n)
glob 限定符)。${f%-*}
:文件名的一部分,直到最右边-
(如果没有,则到最后-
)。$greatest
: 扩展到非空价值观的关联数组。所以这里,对于共享相同根目录的文件,只有编号最大的文件才会被扩展。
答案2
files=(*)
mapfile -t prefixes < <(printf "%s\n" "${files[@]%-*}" | sort -u)
for p in "${prefixes[@]}"; do ls -v "$p"* | tail -1; done
name_file-3.txt
some_other_file-2.txt
然后将它们复制到其他目录:
for ...; done | xargs cp -t /destination/directory
答案3
如果文件位于当前工作目录中并且它们的名称符合示例(数字前面有一个破折号),则以下符合 POSIX 标准的管道应该可以工作:
ls | sort -t- -k1,1 -k2,2rn | awk -F- 'k!=$1 {print; k=$1}' | pax -rw /path/to/dir
如果排序的 -u 选项稳定,则 awk 组件可以替换为 sort -u(以便始终选择集合的第一行来表示该集合)。 POSIX 不需要这种稳定性,但根据其手册,{Free,Net,Open}BSD 和 GNU 实现提供了这种稳定性。如果你喜欢诱人的命运:
ls | sort -t- -k1,1 -k2,2rn | sort -mut- -k1,1 | pax -rw /path/to/dir
无论哪种情况,目标目录都不能位于当前工作目录中。
答案4
我将文件拆分为制表符分隔的部分,以便进行更可靠、可自定义的文件名解析,然后使用 awk 查找每个部分的最高排名并进行报告。在继续下一步之前,请先尝试管道的每个部分!
find DIR -type f <other find criteron> -print |
perl -lne 'print join("\t",(/^(.*?-)(\d+)(\.\w+)$/))' |
awk -F\\t '$2 > f[$1] { f[$1]=$2;e[$1]=$3; } END { for (k in f) { print k f[k] e[k] }}' |
xargs cp -t <desination_directory>
awk 脚本将每个文件名放入关联的数组条目中,始终保留找到的最高排名。扩展名存储在其自己的数组中。处理完所有输入后,将输出所有数组条目,每行一个。该xargs cp -t
行将所有文件复制到您指定的目录。
还有另一种方法行不通如果数字大于 9 并且没有用 0 填充,那就很好。该方法按字典顺序对文件进行排序,然后在解析列表时,第一部分发生变化,使用最近看到的文件名。当文件名是这样的时候,它就不起作用:
file-9.txt
file-10.txt
因为 file-10.txt 将出现在 file-9 之前。上面的 awk 脚本进行了数值比较。
注意:带有制表符和换行符的文件名会导致阻塞。
注意事项 2:如果每个文件名前缀可以有多个扩展名,我们将不得不进行一些调整以使其正确。