5

我正在尝试对 ls 命令输出的一系列文件进行数字排序,这些文件与模式ABCDE1234A1789.RST.txtABCDE12345A1789.RST.txt789”字段匹配。

在上面的示例模式中,ABCDE所有文件都相同,1234或者12345是不同的数字,但长度始终为 4 或 5 位。A1所有文件的长度相同,但值可能会有所不同,因此很遗憾它不能用作分隔符。第一个之后的.所有文件都相同。就像是:

ls -l *.RST.txt | sort -k +9.13 | awk '{print $9} ' > file-list.txt

将匹配较短的文件名,但不匹配较长的文件名,因为我要排序的字段之前的字符长度可变。

有没有办法在不先填充较短长度的文件以使它们的长度相同的情况下完成对所有文件的排序?

4

3 回答 3

4

Perl 来救援!

perl -e 'print "$_\n" for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"'

如果您的 perl 是较新的(5.10 或更新版本),您可以将其缩短为

perl -E 'say for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"'
于 2013-09-04T20:48:19.900 回答
3

由于您已确定为不变的文件名部分,您实际上可以构建一个排序将使用的键:

$ echo ABCDE{99999,8765,9876,345,654,23,21,2,3}A1789.RST.txt \
  | fmt -w1 \
  | sort -tE -k2,2n --debug
ABCDE2A1789.RST.txt
     _
___________________
ABCDE3A1789.RST.txt
     _
___________________
ABCDE21A1789.RST.txt
     __
etc.

这样做是告诉 sort 分隔 character 上的字段E,然后以数字方式使用第二个字段。--debug到达 coreutils 8.6,并且可以非常有助于准确地了解 sort 正在做什么。

于 2013-09-04T23:03:55.913 回答
2

在 bash 中执行此操作的常规方法是提取您的排序字段。除 sort 命令外,以下仅在纯 bash 中实现:

sort_names_by_first_num() {
  shopt -s extglob
  for f; do
    first_num="${f##+([^0-9])}";
    first_num=${first_num%[^0-9]*};
    [[ $first_num ]] && printf '%s\t%s\n' "$first_num" "$f"
  done | sort -n | while IFS='' read -r name; do name=${name#*$'\t'}; printf '%s\n' "$name"; done
}

sort_names_by_first_num *.RST.txt

也就是说,换行分隔文件名(正如这个问题似乎要求的那样)是一种不好的做法: UNIX 文件系统上的文件名允许在其名称中包含换行符,因此在列表中用换行符分隔它们意味着您的列表无法包含有效名称范围的重要子集。对列表进行 NUL 分隔是更好的做法。这样做看起来像这样:

sort_names_by_first_num() {
  shopt -s extglob
  for f; do
    first_num="${f##+([^0-9])}";
    first_num=${first_num%[^0-9]*};
    [[ $first_num ]] && printf '%s\t%s\0' "$first_num" "$f"
  done | sort -n -z | while IFS='' read -r -d '' name; do name=${name#*$'\t'}; printf '%s\0' "$name"; done
}

sort_names_by_first_num *.RST.txt
于 2013-09-04T21:28:43.577 回答