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

有没有办法在UNIX排序中忽略标题行?,unix,sorting,command-line,Unix,Sorting,Command Line,我有一个固定宽度的字段文件,我正试图使用UNIX(在我的例子中是Cygwin)排序实用程序对其进行排序 问题在于,文件顶部有一个两行标题,该标题被排序到文件底部(因为每个标题行都以冒号开头) 有没有一种方法可以告诉sort“将前两行通过未排序”或指定将冒号行排序到顶部的顺序?如果有帮助,剩余的行总是以6位数字开头(这实际上是我正在排序的键) 例如: :0:12345 :1:6:2:3:8:4:2 010005TSTDOG_FOOD01 500123TSTMY_RADAR00 222334NOTA

我有一个固定宽度的字段文件,我正试图使用UNIX(在我的例子中是Cygwin)排序实用程序对其进行排序

问题在于,文件顶部有一个两行标题,该标题被排序到文件底部(因为每个标题行都以冒号开头)

有没有一种方法可以告诉sort“将前两行通过未排序”或指定将冒号行排序到顶部的顺序?如果有帮助,剩余的行总是以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

您可以使用
tail-n+3 |排序…
(tail将从第三行输出文件内容)。

(head-n2和&tail-n+3 |排序)>新建文件

括号创建了一个子shell,将stdout包装起来,这样您就可以对其进行管道传输或重定向,就像它来自单个命令一样。

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

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

使用Python提取数据| awk'NR:

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)

以下是一个适用于管道数据的版本:

(read -r; printf "%s\n" "$REPLY"; sort)
如果标题有多行:

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

这个解决方案来自于

这里是一个bash shell函数,它是从其他答案中派生出来的。它处理文件和管道。第一个参数是stdin的文件名或“-”。其余参数将传递给sort。举几个例子:

$ 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
}
hsort()
{
如果[“$1”=”-h”];则
echo“对文件或标准输入进行排序,将第一行视为标题。”;
echo“第一个参数是文件或'-'表示标准输入。附加”;
echo“要排序的参数跟在第一个参数后面,包括其他文件。”;
echo“文件语法:$hsort File[sort options][File…]”;
echo“STDIN语法:$hsort-[sort options][file…]”;
返回0;
elif[-f“$1”];然后
本地文件=$1;
转移;
(head-n1$file和&tail-n+2$file | sort$*);
elif[“$1”=”-“];然后
转移;
(读取-r;printf“%s\n”$REPLY;排序$*);
其他的
>&2 echo“错误。未找到文件:$1”;
>&2 echo“使用'hsort[sort options]”或'hsort-[sort options]”;
返回1;
fi
}

只需要两行代码

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
-一,

结果:
$cat a.tmp

标题
-1
1
5
8
一百


这与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;

这将满足您的需要。

因此,这里有一个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
}
这会将文件保存到单独的参数中。因为我们要删除最后一个论点

        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
这里我们删除最后一个参数。因为我们不想把它作为排序参数传递

        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
最后,我们执行awk部分,将参数(如果是文件,则减去最后一个参数)传递到awk中进行排序。这是Dave最初提出的,并修改为采用排序参数。我们依赖于这样一个事实,即如果我们使用管道,
$file
将是空的,因此被忽略

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

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

    your_script | (sed -u 1q; sort)
或同等地

    cat your_data | (sed -u 1q; sort)
键位于
1q
——打印第一行(标题)并退出(将其余输入保留为
排序

对于给定的示例,
2q
将实现此目的


-u
开关(无缓冲)对于那些
sed
s(特别是GNU)是必需的,否则它们将以块的形式读取输入,从而消耗您想要通过
sort
进行排序的数据。

记录:我目前使用的命令行是“sort-t\\-k1.1,1.6”[数据可以包含空格,但不会包含反斜杠]谢谢;我接受这个答案,因为它看起来最完整、最简洁(我理解它在做什么!)-应该是“head-n2”,不过:-)谢谢,修复了“head”部分。有没有办法让这个版本在管道数据上工作?我尝试了
tee>(head-n$header\u size)| tail-n+$header|u size | sort
,但head似乎在
tail | sort
管道之后运行,因此页眉最终会打印出来。这是确定的还是竞争条件?您可能会拼凑一些东西,使用
cat
将stdin重定向到临时文件,然后对新文件b运行上述命令但它已经开始变得足够丑陋,使用其他响应中给出的基于awk的解决方案可能会更好。@DamienPollet:See's。预先假设系统安装了Python(我的没有)非常好,它可以处理任意管道,不仅仅是文件!很漂亮,awk总是让我惊讶。而且,你不需要
$0
print
就足够了。@SamWatkins没有那么难看了,-r选项做什么排序?这应该是反向排序吗?这基本上与公认的答案相同吗?(除了BobS的方法将结果放在标准输出上,允许您在写入文件之前通过其他过滤器发送结果,如果需要)很好。对于单头情况,我使用
extract_data |(read h;echo“$h”;sort)
足够短,可以记住。您的示例涵盖了更多边缘情况:这是最好的答案。在管道上工作。没有awk。好吧,我对这一点进行了分析,似乎bash使用了特殊的长度来实现这一点。一般来说,如果用C或其他语言编码,它将不起作用,因为stdio读取的不仅仅是第一行标题。如果在可查找的文件上运行它,bash将读取更大的块(在我的测试中是128字节),然后lseek返回到第一行的末尾。如果在管道上运行它,bash一次读取一个字符,直到它通过行的末尾。很好!如果你只想吃hea
    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
    your_script | (sed -u 1q; sort)
    cat your_data | (sed -u 1q; sort)