shell 按组(多个实体)对值排序

ltqd579y  于 2023-04-21  发布在  Shell
关注(0)|答案(2)|浏览(103)

问题是我想按组排序(例如,由分隔符分隔的3个实体作为一个组)。
由于某些原因,我唯一可以使用的分隔符是NULL字符'\0'
以这个输入示例为例(为了简单起见,每个实体都是一个字符):
'b\0s\0n\0c\0p\0f\0a\0z\0m\0'
结果将是(通过取3个实体的组):
'a\0z\0m\0b\0s\0n\0c\0p\0f\0',因为a〈B〈c。
我知道sort,但不幸的是,它不适用于我试图做的事情,因为它将对每个实体单独排序(没有组)。
一种解决方案是用2个NULL字符分隔不同的组(例如'b\0s\0n\0\0c\0p\0f\0\0a\0z\0m\0\0'),但sort也不是正确的工具,因为它不支持多字符制表符。
所以,现在,我不知道任何解决方案在shell
典型的数据是大约100组3个实体:size\0filepath\0folderpath\0(其中folderpath/filepath是文件的绝对路径)。由于Unix系统中的路径可以包含任何字符(NULL除外),因此我可以使用的唯一分隔符是'\0'
理想情况下,我会喜欢一个代码,排序文件的大小,问题(相对于其他SO问题,我读)是这里的路径可能有'\n'字符。

vwhgwdsa

vwhgwdsa1#

使用GNU awk和包含换行符的输入文件:

$ od -w -An -tx1 file
 62 00 73 00 6e 0a 6e 00 63 00 70 00 66 0a 0a 66 00 61 00 7a 00 6d 0a 0a 0a 6d

$ awk -v RS="\x0" '
    NR % 3 == 1 {i = i + 1}
    { t[i] = t[i] $0 "\x0" }
    END { n = asort(t); for(i = 1; i <= n; i++) printf("%s", t[i]) }
' file | od -w -An -tx1
 61 00 7a 00 6d 0a 0a 0a 6d 00 62 00 73 00 6e 0a 6e 00 63 00 70 00 66 0a 0a 66 00

我们将记录分隔符设置为NUL,用3个标记的组填充数组t,最后我们用asort对数组进行排序并打印。如果默认的排序算法不是你想要的,请参阅https://www.gnu.org/software/gawk/manual/html_node/Array-Sorting-Functions.html以了解有关排序选项的解释。
使用GNU sedsort -z

$ sed -zn 'N;s!\x0! !;N;s!\x0!/!p' file |
  sort -z |
  sed -zn 's! !\x0!;s!/!\x0!p' |
  od -w -An -tx1
 61 00 7a 00 6d 0a 0a 0a 6d 00 62 00 73 00 6e 0a 6e 00 63 00 70 00 66 0a 0a 66 00

如果第一个token是一个size,我们可以假设它不包含空格。如果第二个token是一个filename,我们也可以假设它不包含斜杠。因此,我们对输入进行预处理,将组中的第一个NUL替换为空格,将第二个替换为斜杠sort -z,然后进行后处理以恢复预处理。根据您的特定需求调整sort选项。
请注意,在这两种解决方案中都添加了最终NUL。如果有问题,请使用head -c-1删除它。

vuv7lop3

vuv7lop32#

如果你真的必须坚持只使用空分隔符,传统的Unix文本工具通常不能移植地科普它们。但是许多非传统的,非标准的工具在实践中相当普遍地处理它们。这里是Python 3和Perl的解决方案。

import sys
 
with open(sys.argv[1], 'rb') as fh:
    seq = fh.read().split(b'\x00')
items = [b'\x00'.join(seq[i:i + 3]) for i in range(0, len(seq)-1, 3)]
print(b'\x00'.join(sorted(items)).decode('us-ascii'))

在某些平台上,Python 2可能仍然是默认版本,在这些平台上,工具链的稳定性胜过了便利性、安全性和健壮性。Python 2有一个更简单的字符串类型,能够容纳任意的二进制数据,所以这段代码在Python 2中实际上可能更简单,更接近Perl版本。

perl -0 -ne 'push @rec, $_;
  if ($#rec == 2) { push @items, join("", @rec); @rec = (); }
    END { print(join("", sort @items)) }' file

这两个都将所有数据读入内存,因此如果您的数据比可用RAM多,则会很不方便。
演示:https://ideone.com/Kk7mxH

相关问题