对制表符分隔的文件进行排序

Posted

技术标签:

【中文标题】对制表符分隔的文件进行排序【英文标题】:Sorting a tab delimited file 【发布时间】:2010-11-05 11:02:49 【问题描述】:

我有一个格式如下的数据:

foo<tab>1.00<space>1.33<space>2.00<tab>3

现在我尝试根据最后一个字段对文件进行递减排序。 我尝试了以下命令,但没有按预期排序。

$ sort -k3nr file.txt  # apparently this sort by space as delimiter

$ sort -t"\t" -k3nr file.txt
  sort: multi-character tab `\\t'

$ sort -t "`/bin/echo '\t'`" -k3,3nr file.txt
  sort: multi-character tab `\\t'

正确的做法是什么?

这里是sample data。

【问题讨论】:

【参考方案1】:

使用 bash 就可以了:

$ sort -t$'\t' -k3 -nr file.txt

注意单引号字符串前面的美元符号。你可以阅读关于 它在ANSI-C Quoting sections of the bash man page。

【讨论】:

Use '"'"' 在别名中使用它。 您能展示如何通过此分隔符在 awk 命令中进行排序吗?与awk 'print $0 | "sort -nr" &gt; "outfile" ' datafile 一样,除了将转义的制表符分隔符发送到排序命令。 如果您想要数字排序,请使用 -g 而不是 -n-n 坏了。 也适用于zsh 5.7.1 (x86_64-apple-darwin19.0) 抱歉,我在 Windows 上遇到了同样的问题,是否也有适用于 Windows 的解决方案?除了复制制表符并粘贴之外【参考方案2】:

默认情况下,字段分隔符是非空白到空白的过渡,因此选项卡应该可以正常工作。

但是,这些列的索引基数为 1 和基数为 0,因此您可能需要

sort -k4nr file.txt

按倒序按第 4 列对 file.txt 进行排序。 (虽然问题中的数据甚至有 5 个字段,所以最后一个字段将是索引 5。)

【讨论】:

这只有在制表符分隔的字段之间的空格字符数对于所有输入行都相同时才有效。 对字段中包含空格的有效 tsv 文件通常不起作用的错误 hack。请勿用于生产!【参考方案3】:

您需要在 -t\ 之后放置一个实际的制表符,并在外壳中执行此操作,您先按 ctrl-v,然后按制表符。我用过的大多数 shell 都支持这种文字制表符输入模式。

但请注意,因为从其他地方复制和粘贴通常不会保留标签。

【讨论】:

这是最好的(最便携的)答案。 emacs 还允许您在“引用插入”模式下执行此操作:例如 C-q &lt;tab&gt;。我认为在 nano 中也是 ^V cntrl-q 在 QTerminal 中与 shell 对话。【参考方案4】:

$ 解决方案对我不起作用。 但是,通过实际将制表符本身放入命令中: 排序 -t'' -k2

【讨论】:

使用 &lt;C-v&gt;&lt;Tab&gt; 插入制表符,以防制表符键用于您的外壳中的自动完成。 ANSI 引用 $'\t' 适用于 ksh、zsh 和 bash。 Bourne shell 不支持它。看到这个帖子:unix.stackexchange.com/a/371873/201820【参考方案5】:

通过类似awk ' print print $1"\t"$2"\t"$3"\t"$4"\t"$5 ' 的方式传递它。这会将空格更改为制表符。

【讨论】:

@MB:我需要保持空间完整。 无疑有一种更简洁的方法可以做到这一点,但没有什么能阻止您通过 awk 将其管道化,将空格更改为制表符,对数据进行排序,然后再次通过 awk 管道化,将制表符更改回空格。 如果您要保留混合的制表符和空格,这将不起作用。【参考方案6】:

一般来说,如果可以避免的话,保留这样的数据并不是一件好事,因为人们总是混淆制表符和空格。

使用 Perl、Python 或 Ruby 等脚本语言非常简单地解决您的问题。下面是一些示例代码:

#!/usr/bin/perl -w

use strict;

my $sort_field = 2;
my $split_regex = qr\s+;

my @data;
push @data, "7 8\t 9";
push @data, "4 5\t 6";
push @data, "1 2\t 3";

my @sorted_data = 
    map   $_->[1] 
    sort  $a->[0] <=> $b->[0] 
    map   [ ( split $split_regex, $_ )[$sort_field], $_ ] 
    @data;

print "unsorted\n";
print join "\n", @data, "\n";
print "sorted by $sort_field, lines split by $split_regex\n";
print join "\n", @sorted_data, "\n";

【讨论】:

【参考方案7】:

我想要一个 Windows 上的 Gnu 排序解决方案,但上述解决方案在命令行上都不适合我。

使用 Lloyd 的线索,以下批处理文件 (.bat) 对我有用。

在双引号内键入制表符。

C:\>cat foo.bat

sort -k3 -t"    " tabfile.txt

【讨论】:

是的,这里的诀窍是把它放在一个 .bat 文件中,否则它将不起作用【参考方案8】:

当我使用“general-numeric-sort”时,我在 bash shell 中的 cygwin 中遇到了这个问题。如果我指定 -t$'\t' -kFg,其中 F 是字段编号,它不起作用,但是当我同时指定 -t$'\t'-kF,Fg(例如 -k7,7g 用于第 7 个字段)时,它确实起作用了。没有-t$'\t'-kF,Fg 不起作用。

【讨论】:

【参考方案9】:

如果您想让自己更轻松地只使用制表符,请将空格替换为制表符:

tr " " "\t" < <file> | sort <options>

【讨论】:

我的 tr 不读取文件,只读取流 XD。 usage: tr [-Ccsu] string1 string2 tr string1 string2 &lt;some-file。任何东西都可以读取文件,只要它可以读取标准输入。【参考方案10】:

Lars Haugseth 的回答只能在命令行中为我工作,如果从 shell 脚本执行它会给出此错误:

排序:多字符制表符'$\t'

如果有人在看,如果它是在 shell 脚本中编码的解决方案是

sort -t'    '

制表符在引号之间。

【讨论】:

【参考方案11】:

sort -t "$(printf '\t')" 为我工作

【讨论】:

以上是关于对制表符分隔的文件进行排序的主要内容,如果未能解决你的问题,请参考以下文章

如何对文件进行子集化 - 选择多个行或列

将大制表符分隔文件读入R [重复]

向 Grep 添加制表符分隔符

cut命令

根据第一列组合几个制表符分隔文件的某些列

没有类或结构的 C++ 排序数据类型