【问题标题】:Is there a way to ignore header lines in a UNIX sort?有没有办法在 UNIX 排序中忽略标题行?
【发布时间】:2013-01-28 12:49:33
【问题描述】:

我有一个固定宽度字段文件,我正在尝试使用 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

【问题讨论】:

标签: unix sorting command-line


【解决方案1】:

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

    your_script | (sed -u 1q; sort)

或等价的,

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

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

对于给出的示例,2q 可以解决问题。

seds(尤其是 GNU 的)需要 -u 开关(无缓冲),否则它们会以块的形式读取输入,从而消耗您想要通过 sort 代替的数据。

【讨论】:

  • 嗨,@Andrea;欢迎来到堆栈溢出。恐怕你的答案不起作用,至少当我在 Windows 上的 Git Bash 中测试它时(我已经从 Cygwin 继续前进,我在 6 年前使用不同的工作的 shell)。 sed 命令将所有数据从标准输入中拉出,不留任何数据传递给排序。尝试将命令更改为 cat your_data | (sed 1q ; wc -l) 看看我的意思。
  • 如果您第二次将输入传递给 sed 命令,这可能会起作用,如下所示: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn) > sorted.csv
  • IMO 这是这里最简单的解决方案,也最容易记住。它适用于没有特殊考虑或笨拙的引用和转义的管道数据,并且如果您通过带有 -s 标志的管道排序命令链对多个列进行排序,则不需要多次使用。例如。 bgzip -dc somefile.tsv.gz | (sed -u 2q; sort -k 3,3 -n | sort -k 2,2 -n -s | sort -k 1,1 -s) | bgzip -c > my_sorted_file.tsv.gz。关键是添加-u 标志的编辑,这应该解决了@RobGilliam 的上述问题。
  • 你能解释一下管道和括号是如何工作的吗?
【解决方案2】:
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

括号创建了一个子shell,将标准输出包裹起来,以便您可以通过管道传输或重定向它,就好像它来自单个命令一样。

【讨论】:

  • 谢谢;我接受这个答案,因为它看起来最完整和简洁(我理解它在做什么!) - 它应该是“head -n 2”,虽然:-)
  • 有没有办法让这个版本在管道数据上工作?我尝试使用tee &gt;(head -n $header_size) | tail -n +$header_size | sort,但head 似乎在tail|sort 管道之后运行,因此最后打印了header。这是确定性还是竞争条件?
  • 你可以拼凑一些东西,你使用cat 将标准输入重定向到一个临时文件,然后在那个新文件上运行上面的命令,但它开始变得丑陋到可能更好使用其他响应中给出的基于 awk 的解决方案之一。
  • @DamienPollet:见Daveanswer
  • @DamienPollet:见freeseek'sanswer
【解决方案3】:

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

例如。

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

这会逐字打印前两行并将其余行通过sort 传递。

请注意,这具有非常特殊的优势,即能够有选择地对零件进行排序 的管道输入。建议的所有其他方法只会对可以多次读取的普通文件进行排序。这适用于任何事情。

【讨论】:

  • 非常好,它适用于任意管道,而不仅仅是文件!
  • 美丽,awk 从未停止让我惊讶。另外,你不需要$0print 就足够了。
  • @SamWatkins freeseek's answer 不那么丑。
  • -r 选项对排序有什么作用?这应该是反向排序吗?
【解决方案4】:

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

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

如果您的标题有多行:

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

此解决方案来自here

【讨论】:

  • 不错。对于我使用extract_data | (read h; echo "$h"; sort) 的单个标题情况,它足够短,可以记住。你的例子涵盖了更多的边缘情况。 :) 这是最好的答案。在管道上工作。没有 awk。
  • 好的,我对此进行了跟踪,似乎 bash 为完成这项工作付出了特殊的努力。一般来说,如果你用 C 或其他语言编写它,它将无法工作,因为 stdio 将读取的不仅仅是第一个标题行。如果你在一个可查找的文件上运行它,bash 会读取一个更大的块(在我的测试中是 128 字节),然后 lseeks 回到第一行结束之后。如果在管道上运行它,bash 一次读取一个字符,直到它通过行尾。
  • 不错!如果你只是想吃头,那就更容易记住了:extract_data | (read; sort)
  • 这个几乎是完美的,但你需要使用“IFS= read”而不是“read”来保持前导和尾随空格。
  • 我认为这应该是公认的答案。简单、简洁且更灵活,因为它也适用于管道数据。
【解决方案5】:

所以这是一个 bash 函数,其中参数与排序完全相同。支持文件和管道。

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

【讨论】:

    【解决方案6】:

    你可以使用tail -n +3 &lt;file&gt; | sort ...(tail会从第3行输出文件内容)。

    【讨论】:

    • 但这会丢失标题,这是不希望的。
    【解决方案7】:

    只需要两行代码...

    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

    【讨论】:

    • 这不是和接受的答案基本相同的答案吗? (除了 BobS 的方法将结果放在标准输出上,如果需要,您可以在将结果写入文件之前通过其他过滤器发送结果)
    【解决方案8】:
    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
    

    【讨论】:

      【解决方案9】:
      cat file_name.txt | sed 1d | sort 
      

      这会做你想做的。

      【讨论】:

      • 1) 这只会删除标题行并对其余部分进行排序,它不会对标题行下方的所有内容进行排序,从而使标题保持不变。 2)当标题实际上是两行时,它只删除第一行(阅读问题)。 3)为什么在“sed 1d
      【解决方案10】:

      使用 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(我的没有)
      【解决方案11】:

      这是从其他答案派生的 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
      }
      

      【讨论】:

        【解决方案12】:

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

        【讨论】:

          猜你喜欢
          • 2020-07-02
          • 1970-01-01
          • 2016-12-27
          • 2015-04-17
          • 2011-09-10
          • 1970-01-01
          • 2011-10-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多