有没有办法忽略UNIX排序中的标题行?


102

我有一个固定宽度字段文件,我正在尝试使用UNIX(在我的情况下为Cygwin)排序实用程序进行排序。

问题是文件顶部有两行标题,该标题被排序到文件底部(每个标题行以冒号开头)。

有没有办法告诉排序“使未排序的前两行通过”或指定将冒号行排序到顶部的顺序-其余行始终以6位数字开头(这实际上是键(正在排序),如果有帮助的话。

例:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
500123TSTMY_RADAR00
222334NOTALINEOUT01
477821USASHUTTLES21
325611LVEANOTHERS00

应该排序为:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00

记录一下:到目前为止,我使用的命令行是“ sort -t \\ -k1.1,1.6 <file>” [数据可以包含空格,但绝不包含反斜杠]
Rob Gilliam

Answers:


124
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

括号会创建一个子外壳,将标准输出包裹起来,以便您可以对其进行管道传输或重定向,就好像它来自单个命令一样。


谢谢; 我正在接受这个答案,因为它似乎是最完整,最简洁的(我知道它在做什么!)-应该是“ head -n 2”,但:-)
Rob Gilliam 2013年

1
谢谢,修复了“头部”部分。
BobS 2013年

4
有没有办法让此版本对管道数据起作用?我尝试使用tee >(head -n $header_size) | tail -n +$header_size | sort,但是head似乎在tail|sort管道后面运行,因此标题最终打印在末尾。这是确定性还是竞赛条件?
Damien Pollet 2014年

您可能会拼凑出一些cat用于将stdin重定向到临时文件的内容,然后在该新文件上运行上述命令,但是它开始变得很难看,因此最好使用提供的基于awk的解决方案之一其他回应。
BobS 2014年

@DamienPollet:请参阅Dave答案
Jonathan Leffler

63

如果您不介意使用awk,则可以利用awk的内置管道功能

例如。

extract_data | awk 'NR<3{print $0;next}{print $0| "sort -r"}' 

这将逐字打印前两行,并将其余的行通过管道传输sort

请注意,这具有非常特殊的优势,即能够选择性地对管道输入的各个部分进行排序。建议的所有其他方法将仅对可多次读取的纯文件排序。这适用于任何东西。


2
非常好,它适用于任意管道,不仅文件!
lapo 2014年

4
美丽,awk永远不会令我惊讶。另外,您不需要$0print就足够了。
nachocab 2015年

1
@SamWatkins freeseek的 回答不太丑。
fess。

-r选项在做什么以进行排序?这应该是反向排序吗?
gvrocha 2015年

32

这是适用于管道数据的版本:

(read -r; printf "%s\n" "$REPLY"; sort)

如果标题有多行:

(for i in $(seq $HEADER_ROWS); do read -r; printf "%s\n" "$REPLY"; done; sort)

这个解决方案是从这里


9
很好 对于单个标题的情况,我用 extract_data | (read h; echo "$h"; sort) 它足够短以便记住。您的示例涵盖了更多的极端情况。:)这是最好的答案。在管道上工作。没有awk。
fess。

1
好的,我把它拉直了,看来bash达到了一定的长度才能使它起作用。通常,如果您使用C或其他语言编写此代码,则它将无法正常工作,因为stdio读取的内容不只是第一行标题行。如果在可搜索的文件上运行它,则bash会读取更大的块(在我的测试中为128字节),然后lseek返回第一行的末尾。如果在管道上运行它,则bash一次读取一个字符,直到它通过该行的末尾。
山姆·沃特金斯2015年

真好!如果您只想吃标头,就更容易记住:extract_data | (read; sort)
JasonSuárez17年

这个几乎是完美的,但是您需要使用“ IFS =读取”而不是“读取”来保留前导和尾随空格。
Stanislav German-Evtushenko

6
我认为这应该是公认的答案。简单,简洁和更灵活,因为它也适用于管道数据。
Paul I

12

在简单的情况下,sed可以优雅地完成工作:

    your_script | (sed -u 1q; sort)

或等效地,

    cat your_data | (sed -u 1q; sort)

键位于1q-打印第一行(标题)并退出(将其余输入留给sort)。

对于给出的示例,2q将达到目的。

-u开关(无缓冲)所需的那些sedS(值得注意的是,GNU的),否则将读取输入的数据块,从而消耗数据要经过sort代替。


1
嗨,@ Andrea; 欢迎使用Stack Overflow。恐怕您的答案不起作用,至少当我在Windows上的Git Bash中对其进行测试时(至少从我使用Cygwin(六年前我使用另一种工作的shell)转移过来)时,您的答案无效。sed命令将所有数据从标准输入中提取出来,而没有任何数据可以传递给排序。尝试将命令更改为cat your_data | (sed 1q; wc -l)来了解我的意思。
罗布·吉利姆

1
如果您第二次将输入传递给sed命令,则可能会起作用,例如:cat sortMe.csv | (sed 1q sortMe.csv; sort -t,-k3 -rn)> sorted.csv
Harry Cramer


4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

例:

> cat temp
10
8
1
2
3
4
5
> head -2 temp && nawk 'NR>2' temp | sort -r
10
8
5
4
3
2
1

3

只需要两行代码...

head -1 test.txt > a.tmp; 
tail -n+2 test.txt | sort -n >> a.tmp;

对于数字数据,需要-n。对于alpha排序,不需要-n。

示例文件:
$ cat test.txt

接头
8
5
100
1
-1

结果:
$ cat a.tmp

标题
-1
1
5
8
100


1
这与接受的答案基本不一样吗?(除了BobS的方法将结果放在stdout上,如果需要的话,允许您在通过其他过滤器发送结果之前将结果发送到文件中)
Rob Gilliam

1

因此,这是一个bash函数,其中的参数完全类似于sort。支持文件和管道。

function skip_header_sort() {
    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then
        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
    fi
    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file
}

这个怎么运作。此行检查是否至少有一个参数,最后一个参数是否是文件。

    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then

这会将文件保存为单独的参数。由于我们即将删除最后一个参数。

        local file=${@: -1}

在这里,我们删除最后一个参数。由于我们不想将其作为排序参数传递。

        set -- "${@:1:$(($#-1))}"

最后,我们执行awk部分,传递参数(如果是文件则减去最后一个参数)以进行awk排序。这是戴夫(Dave)最初提出的,并进行了修改以接受排序参数。我们依靠这样的事实:$file如果要进行管道传输,它将为空,因此被忽略。

    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file

逗号分隔文件的示例用法。

$ cat /tmp/test
A,B,C
0,1,2
1,2,0
2,0,1

# SORT NUMERICALLY SECOND COLUMN
$ skip_header_sort -t, -nk2 /tmp/test
A,B,C
2,0,1
0,1,2
1,2,0

# SORT REVERSE NUMERICALLY THIRD COLUMN
$ cat /tmp/test | skip_header_sort -t, -nrk3
A,B,C
0,1,2
2,0,1
1,2,0

0

使用Python:

import sys
HEADER_ROWS=2

for _ in range(HEADER_ROWS):
    sys.stdout.write(next(sys.stdin))
for row in sorted(sys.stdin):
    sys.stdout.write(row)

前提是系统已安装Python(我没有安装)
Rob Gilliam 2014年

0

这是从其他答案派生的bash shell函数。它同时处理文件和管道。第一个参数是标准名称的文件名或“-”。其余参数将传递给排序。几个例子:

$ hsort myfile.txt
$ head -n 100 myfile.txt | hsort -
$ hsort myfile.txt -k 2,2 | head -n 20 | hsort - -r

shell功能:

hsort ()
{
   if [ "$1" == "-h" ]; then
       echo "Sort a file or standard input, treating the first line as a header.";
       echo "The first argument is the file or '-' for standard input. Additional";
       echo "arguments to sort follow the first argument, including other files.";
       echo "File syntax : $ hsort file [sort-options] [file...]";
       echo "STDIN syntax: $ hsort - [sort-options] [file...]";
       return 0;
   elif [ -f "$1" ]; then
       local file=$1;
       shift;
       (head -n 1 $file && tail -n +2 $file | sort $*);
   elif [ "$1" == "-" ]; then
       shift;
       (read -r; printf "%s\n" "$REPLY"; sort $*);
   else
       >&2 echo "Error. File not found: $1";
       >&2 echo "Use either 'hsort <file> [sort-options]' or 'hsort - [sort-options]'";
       return 1 ;
   fi
}

0

这与Ian Sherbin的答案相同,但我的实现是:-

cut -d'|' -f3,4,7 $arg1 | uniq > filetmp.tc
head -1 filetmp.tc > file.tc;
tail -n+2 filetmp.tc | sort -t"|" -k2,2 >> file.tc;

-4
cat file_name.txt | sed 1d | sort 

这将做您想要的。


1)这只会删除标题行并对其余部分进行排序,不会对标题行下方的所有内容进行排序,而使标题保持完整。2)当标题实际上是两行时,它仅删除第一行(请阅读问题)。3)为什么当“ sed 1d <file_name.txt”或什至只是“ sed 1d file_name.txt”具有相同效果时,为什么使用“ cat file_name.txt | sed 1d”?
罗布·吉利姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.