排序但保持标题行在顶部

jon*_*rry 78 text-processing sort table

我从一个程序中获取输出,该程序首先生成一行,即一堆列标题,然后是一堆数据行。我想剪切此输出的各个列并查看它根据各个列排序。在没有标题的情况下,通过-k选择与列sort一起cutawk查看列的子集,可以轻松完成剪切和排序。但是,这种排序方法将列标题与输出的其余行混合在一起。有没有一种简单的方法可以将标题保持在顶部?

Mik*_*kel 77

窃取 Andy 的想法并使其成为一个函数,以便更易于使用:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}
Run Code Online (Sandbox Code Playgroud)

现在我可以这样做:

$ ps -o pid,comm | body sort -k2
  PID COMMAND
24759 bash
31276 bash
31032 less
31177 less
31020 man
31167 man
...

$ ps -o pid,comm | body grep less
  PID COMMAND
31032 less
31177 less
Run Code Online (Sandbox Code Playgroud)

  • 从 `header` 重命名为 `body`,因为您正在对身体进行操作。希望这更有意义。 (4认同)
  • 请记住在所有后续管道参与者上调用 `body`:`ps -o pid,comm | 身体grep 少| 身体排序-k1nr` (3认同)
  • @Tim 你可以只写 `<foo body sort -k2` 或 `body sort -k2 <foo`。只需从您想要的一个额外字符。 (2认同)

And*_*ndy 47

您可以使用 bash 将标题保持在顶部:

command | (read -r; printf "%s\n" "$REPLY"; sort)
Run Code Online (Sandbox Code Playgroud)

或者用 perl 来做:

command | perl -e 'print scalar (<>); print sort { ... } <>'
Run Code Online (Sandbox Code Playgroud)

  • `IFS=` 在读取输入时禁用分词。我认为阅读 `$REPLY` 时没有必要。如果设置了`xpg_echo`(不是默认设置),`echo` 将展开反斜杠转义;在这种情况下,`printf` 更安全。`echo $REPLY` 不带引号会压缩空格;我认为 `echo "$REPLY"` 应该没问题。如果输入可能包含反斜杠转义,则需要 `read -r`。其中一些可能取决于 bash 版本。 (3认同)
  • +1 真棒。我认为值得捆绑为一个 shell 函数。 (2认同)
  • +1,为什么子shell更可取,或者`{}`可以代替`()`的任何原因? (2认同)

小智 38

我发现了一个很好的 awk 版本,它在脚本中运行良好:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个,但它需要一些解释 - 管道在 awk 脚本中。这是如何运作的?它是在外部调用 `sort` 命令吗?有谁知道至少有一个链接到解释 awk 中管道使用的页面? (3认同)

fre*_*eeB 7

moreutilspee中的命令是为此类任务而设计的。

例子:

要保留一个标题行,并对第二个(数字)列进行排序stdin

<your command> | pee 'head -n 1' 'tail -n +2 | sort -k 2,2 -n'
Run Code Online (Sandbox Code Playgroud)

解释:

pee:将 stdin 通过管道传输到一个或多个命令并连接结果。

head -n 1:打印标准输入的第一行。

tail -n +2:从标准输入打印第二行及以下行。

sort -k 2,2 -n:按第二列数字排序。

测试:

printf "header\na 1\nc 3\nb 2\n" | pee 'head -n 1' 'tail -n +2 | sort -k 2,2 -n'
Run Code Online (Sandbox Code Playgroud)

给出

header
a 1
b 2
c 3
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案,因为它很容易记住:我只需要记住“pee”,然后使用我已经知道的常规命令,例如“head”或“sort”。这也使得它可以轻松适应其他用例。多谢! (2认同)

Gil*_*il' 5

Hackish 但有效:在排序之前添加0到所有标题行和1所有其他行。排序后去掉前缀。

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-
Run Code Online (Sandbox Code Playgroud)