Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am getting output from a program that first produces one line that is a bunch of column headers, and then a bunch of lines of data. I want to cut various columns of this output and view it sorted according to various columns. Without the headers, the cutting and sorting is easily accomplished via the -k option to sort along with cut or awk to view a subset of the columns. However, this method of sorting mixes the column headers in with the rest of the lines of output. Is there an easy way to keep the headers at the top?

share|improve this question
    
I came across the following link. However, I can't get this technique of { head -1; sort; } to work. It always deletes a bunch of the text after the first line. Does anyone know why this happens? –  jonderry Apr 23 '11 at 1:02
    
I suspect it's because head is reading more than one line into a buffer and throwing most of it away. My sed idea had the same problem. –  Andy Apr 23 '11 at 1:09

6 Answers 6

up vote 20 down vote accepted

Stealing Andy's idea and making it a function so it's easier to use:

# 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"
    "$@"
}

Now I can do:

$ 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
share|improve this answer
    
ps -C COMMAND may be more appropriate than grep COMMAND, but it's just an example. Also, you can't use -C if you also used another selection option such as -U. –  Mikel Apr 23 '11 at 0:51
    
Or maybe it should be called body? As in body sort or body grep. Thoughts? –  Mikel Apr 23 '11 at 0:57
    
I tried read in this form first, but noticed that it was eating leading whitespace. Making this a function is a good idea. +1 –  Andy Apr 23 '11 at 1:01
    
Renamed from header to body, because you're doing the action on the body. Hopefully that makes more sense. –  Mikel Apr 23 '11 at 1:02
    
@Andy, yeah, we should set IFS=. Fixed. –  Mikel Apr 23 '11 at 1:04

You can keep the header at the top like this with bash:

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

Or do it with perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'
share|improve this answer
1  
+1 awesome. Worth bundling up as a shell function I think. –  Mikel Apr 23 '11 at 0:42
    
+1, any reason why a subshell is preferable, or is {} ok instead of ()? –  jonderry Apr 23 '11 at 0:57
2  
IFS= disables word splitting when reading the input. I don't think it's necessary when reading to $REPLY. echo will expand backslash escapes if xpg_echo is set (not the default); printf is safer in that case. echo $REPLY without quotes will condense whitespace; I think echo "$REPLY" should be okay. read -r is needed if the input may contain backslash escapes. Some of this might depend on bash version. –  Andy Apr 23 '11 at 1:50
1  
@Andy: Wow, you're right, different rules for read REPLY; echo $REPLY (strips leading spaces) and read; echo $REPLY (doesn't). –  Mikel Apr 23 '11 at 2:44
1  
@Andy: IIRC, the default value of xpg_echo depends on your system, e.g. on Solaris I think it defaults to true. This is why Gilles likes printf so much: it's the only thing with predictable behavior. –  Mikel Apr 23 '11 at 2:47

Hackish but effective: prepend 0 to all header lines and 1 to all other lines before sorting. Strip the first character after sorting.

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-
share|improve this answer

Here's some magic perl line noise that you can pipe your output through to sort everything but keep the first line at the top: perl -e 'print scalar <>, sort <>;'

share|improve this answer

I found a nice awk version that works nicely in scripts:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'
share|improve this answer
command | head -1; command | tail -n +2 | sort
share|improve this answer
2  
This starts command two times. Therefore it is limited to some specific commands. However, for the requested ps command in the example, it would work. –  jofel May 20 at 12:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.