如何在 bash 中按照目录按特定顺序列出文件?

如何在 bash 中按照目录按特定顺序列出文件?

我有一堆目录,每个目录都包含一堆文件。通常我可以使用列出所有文件, ls */*.* 但这总是按照目录中的字母顺序列出它们,然后按照每个目录中的字母顺序列出它们。所以它总是会输出

dir1/file1.foo
dir1/file2.foo
...
dir2/file1.foo
dir2/file2.foo
...
...
dirN/file1.foo
dirN/file2.foo
...

我希望它做的是输出它,以便目录按某些特定顺序排列(例如,反向排列),但每个目录中的所有文件都按正常顺序排列,如下所示:

dirN/file1.foo
dirN/file2.foo
...
...
dir1/file1.foo
dir1/file2.foo
...

使用ls -r */*.*不能达到我想要的效果,因为它会反转每个目录中的顺序以及目录本身的顺序。我尝试过使用,它可以ls `ls -r`对没有空格的目录名执行我想要的操作,但对于有空格的目录名(不幸的是大多数目录名都有空格)不起作用。如果我尝试使用,ls "`ls -r`"它会输出与目录名列表相同的内容ls -r,但顺序相反。如何让 bash 执行我想要的操作?

答案1

使用sort命令:

ls */* | sort -rst '/' -k1,1

和:

  • -t '/'更改字段分隔符
  • -r进行反向排序
  • -s确保排序稳定(换句话说,它不会改变指定列之外的顺序
  • -k1,1将排序限制在第一列

这假设的输出ls */*已经排序(应该是),否则sort在中间添加一个简单的阶段。

答案2

通过 zsh 脚本

和 Linux/Unix 中的大多数事物一样,做事的方法不止一种。无论好坏,不管其价值如何,这都是做同样事情的另一种方式。

#!/usr/bin/env zsh
Dirs=(*(/D))  # (/D) = /(only directories) D(include hidden or dot)
for D in ${(O)Dirs}  # (O) = reverse sort
do
    L=(${D}/*(ND))  # (ND) = N(in case the glob is empty) D(see above)
    [ ${#L} -eq 0 ] && { print "${D}/" ; continue }  # if empty glob
    print -l ${(o)L}  # (o) = normal sort
done

printzsh 内置命令-l将每个元素输出到各自的行上。

D如果不想列出隐藏的目录和文件,请删除glob 限定符。

答案3

补充@xeniod 的答案(开始是评论,但太长了):按照最初的要求,按相反顺序对目录进行排序,并按字母数字顺序对文件进行排序:

$ ls -d */* | sort -s -t '/' -k1,1rn -k2,2

或者,如果文件/目录有多个数字,例如,不仅仅是dir1..dir9,还有dir10, dir11, dir101,...和文件file1..file10..file101...,您可以使用选项按“版本号”排序-V,而不是只按字母/数字排序(仅支持 GNU 排序):

$ ls -d */* | sort -s -t '/' -k1,1rV -k2,2V

不过,理想情况下,您的目录和文件会有一些特殊字符作为前缀(或分隔)您想要排序的字段,如破折号(“ -”),例如dir-23/file-1.txtdir-902/file-203.txt等等,这样您就可以只对这些字段进行排序(数字后的非数字文本将被忽略):

$ ls -d */* | sort -s -t- -k2,2nr  -k3,3n

关键点:

  • 在分隔符之间分别添加多个-k{begin,end}排序列/,例如,将第一列按字母数字排序,第二列按反向数字排序,第三列按“版本号”排序,等等
  • -d在“ ”中添加了“ ls -d */*”,这样就不会列出目录内容,如果存在目录内容,则会破坏排序。或者,仅有的考虑文件,可以使用find */ -maxdepth 1 -type f而不是ls。)
  • 请注意,您实际上根本不关心排序中的“/”,这通常没问题。请注意,-t {delim}无论如何,您不能在单个排序中使用多个选项(例如-t '-' -k... -t '/'...

相关内容