sắp xếp nhưng giữ dòng tiêu đề ở trên cùng


56

Tôi đang nhận đầu ra từ một chương trình đầu tiên tạo ra một dòng là một loạt các tiêu đề cột và sau đó là một loạt các dòng dữ liệu. Tôi muốn cắt các cột khác nhau của đầu ra này và xem nó được sắp xếp theo các cột khác nhau. Không có các tiêu đề, việc cắt và sắp xếp dễ dàng được thực hiện thông qua -ktùy chọn sortcùng với cuthoặc awkđể xem một tập hợp con của các cột. Tuy nhiên, phương pháp sắp xếp này trộn các tiêu đề cột với phần còn lại của các dòng đầu ra. Có một cách dễ dàng để giữ các tiêu đề ở đầu?


1
Tôi đã đi qua các liên kết sau đây . Tuy nhiên, tôi không thể có được kỹ thuật này { head -1; sort; }để làm việc. Nó luôn xóa một loạt các văn bản sau dòng đầu tiên. Có ai biết tại sao điều này xảy ra?
jonderry

1
Tôi nghi ngờ đó là vì headđang đọc nhiều hơn một dòng vào bộ đệm và ném hầu hết dòng đó đi. sedÝ tưởng của tôi có cùng một vấn đề.
Andy

@jonderry - kỹ thuật đó chỉ hoạt động với lseekkhả năng nhập liệu nên nó sẽ không hoạt động khi đọc từ một đường ống. Nó sẽ hoạt động nếu bạn chuyển hướng đến một tệp >outfilevà sau đó chạy{ head -n 1; sort; } <outfile
don_crissti

Câu trả lời:


58

Ăn cắp ý tưởng của Andy và biến nó thành một chức năng để dễ sử dụng hơn:

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

Bây giờ tôi có thể làm:

$ 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

ps -C COMMANDcó thể phù hợp hơn grep COMMAND, nhưng đó chỉ là một ví dụ. Ngoài ra, bạn không thể sử dụng -Cnếu bạn cũng sử dụng một tùy chọn lựa chọn khác, chẳng hạn như -U.
Mikel

Hoặc có lẽ nó nên được gọi body? Như trong body sorthoặc body grep. Suy nghĩ?
Mikel

3
Đổi tên từ headerthành body, bởi vì bạn đang thực hiện hành động trên cơ thể. Hy vọng rằng có ý nghĩa hơn.
Mikel

2
Hãy nhớ kêu gọi bodytất cả những người tham gia đường ống tiếp theo:ps -o pid,comm | body grep less | body sort -k1nr
giám mục

1
@Tim Bạn chỉ có thể viết <foo body sort -k2hoặc body sort -k2 <foo. Chỉ cần thêm một nhân vật từ những gì bạn muốn.
Mikel

37

Bạn có thể giữ tiêu đề ở đầu như thế này với bash:

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

Hoặc làm điều đó với perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'

2
+1 tuyệt vời. Tôi nghĩ rằng nó có giá trị như một hàm shell.
Mikel

1
+1, bất kỳ lý do tại sao một subshell là thích hợp hơn, hoặc là {}thay vì ()?
jonderry

2
IFS=vô hiệu hóa tách từ khi đọc đầu vào. Tôi không nghĩ rằng nó cần thiết khi đọc đến $REPLY. echosẽ mở rộng thoát dấu gạch chéo ngược nếu xpg_echođược đặt (không phải mặc định); printflà an toàn hơn trong trường hợp đó. echo $REPLYkhông có trích dẫn sẽ ngưng tụ khoảng trắng; Tôi nghĩ echo "$REPLY"nên ổn thôi. read -rlà cần thiết nếu đầu vào có thể chứa dấu gạch chéo ngược. Một số điều này có thể phụ thuộc vào phiên bản bash.
Andy

1
@Andy: Wow, bạn đúng, các quy tắc khác nhau cho read REPLY; echo $REPLY(dải không gian hàng đầu) và read; echo $REPLY(không).
Mikel

1
@Andy: IIRC, giá trị mặc định xpg_echophụ thuộc vào hệ thống của bạn, ví dụ như trên Solaris tôi nghĩ nó mặc định là đúng. Đây là lý do tại sao Gilles thích printfrất nhiều: đó là điều duy nhất có hành vi có thể dự đoán được.
Mikel

23

Tôi tìm thấy một phiên bản awk đẹp hoạt động độc đáo trong các kịch bản:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'

1
Tôi thích điều này, nhưng nó đòi hỏi một chút giải thích - đường ống nằm trong kịch bản awk. Làm thế nào mà làm việc? Có phải nó đang gọi sortlệnh bên ngoài? Có ai biết ít nhất một liên kết đến một trang giải thích việc sử dụng đường ống trong awk không?
tự đại diện

@Wildcard bạn có thể kiểm tra trang hướng dẫn chính thức hoặc trang mồi này .
lapo

4

Hackish nhưng hiệu quả: thêm vào 0tất cả các dòng tiêu đề và 1tất cả các dòng khác trước khi sắp xếp. Dải ký tự đầu tiên sau khi sắp xếp.

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

3

Dưới đây là một số nhiễu dòng perl ma thuật mà bạn có thể dẫn đầu ra của mình để sắp xếp mọi thứ nhưng vẫn giữ dòng đầu tiên ở trên cùng: perl -e 'print scalar <>, sort <>;'


2

Tôi đã thử command | {head -1; sort; }giải pháp và có thể xác nhận rằng nó thực sự làm hỏng mọi thứ - headđọc thành nhiều dòng từ đường ống, sau đó chỉ xuất ra dòng đầu tiên. Vì vậy, phần còn lại của đầu ra, head không đọc, được chuyển đến - sortKHÔNG NÊN phần đầu ra còn lại bắt đầu từ dòng 2!

Kết quả là bạn bị thiếu các dòng (và một dòng một phần!) Ở đầu đầu ra lệnh của bạn (ngoại trừ bạn vẫn có dòng đầu tiên) - một thực tế rất dễ xác nhận bằng cách thêm một đường ống wcvào cuối đường ống trên - nhưng điều đó cực kỳ khó theo dõi nếu bạn không biết điều này! Tôi đã dành ít nhất 20 phút để cố gắng tìm ra lý do tại sao tôi có một dòng một phần (100 byte đầu tiên hoặc lâu hơn bị cắt) trong đầu ra của tôi trước khi giải quyết nó.

Điều cuối cùng tôi đã làm, hoạt động rất hay và không yêu cầu chạy lệnh hai lần, là:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile
sed 1d $myfile | sort

rm $myfile

Nếu bạn cần đặt đầu ra vào một tệp, bạn có thể sửa đổi điều này thành:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile > outputfile
sed 1d $myfile | sort >> outputfile

rm $myfile

Bạn có thể sử dụng headnội dung dựng sẵn của ksh93 hoặc linetiện ích (trên các hệ thống vẫn còn một) gnu-sed -u qhoặc IFS=read -r line; printf '%s\n' "$line", đọc một byte đầu vào tại một thời điểm để tránh điều đó.
Stéphane Chazelas

1

Tôi nghĩ rằng điều này là dễ dàng nhất.

ps -ef | ( head -n 1 ; sort )

hoặc cái này có thể nhanh hơn vì nó không tạo ra một vỏ con

ps -ef | { head -n 1 ; sort ; }

Công dụng tuyệt vời khác

xáo trộn dòng sau hàng tiêu đề

cat file.txt |  ( head -n 1 ; shuf )

dòng ngược sau hàng tiêu đề

cat file.txt |  ( head -n 1 ; tac )

2
Xem unix.stackexchange.com/questions/11856/ . Đây không thực sự là một giải pháp tốt.
tự đại diện

1
Không hoạt động, cat file | { head -n 1 ; sort ; } > file2chỉ hiển thị đầu
Peter Krauss

0
command | head -1; command | tail -n +2 | sort

4
Điều này bắt đầu commandhai lần. Do đó, nó bị giới hạn trong một số lệnh cụ thể. Tuy nhiên, đối với pslệnh được yêu cầu trong ví dụ, nó sẽ hoạt động.
jofel

0

Đơn giản và dễ hiểu!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' chỉ định dòng số. và 'd' là viết tắt của xóa.

1
Giống như jofel đã bình luận một năm rưỡi trước về câu trả lời của Sarva, điều này bắt đầu commandhai lần. Vì vậy, không thực sự thích hợp để sử dụng trong một đường ống.
tự đại diện

0

Tôi đến đây để tìm kiếm một giải pháp cho lệnh w. Lệnh này hiển thị chi tiết về người đã đăng nhập và những gì họ đang làm.

Để hiển thị kết quả được sắp xếp, nhưng với các tiêu đề được giữ ở trên cùng (có 2 dòng tiêu đề), tôi đã giải quyết:

w | head -n 2; w | tail -n +3 | sort

Rõ ràng điều này chạy lệnh whai lần và do đó có thể không phù hợp cho tất cả các tình huống. Tuy nhiên, với lợi thế của nó, nó dễ nhớ hơn nhiều.

Lưu ý rằng tail -n +3phương tiện 'hiển thị tất cả các dòng từ thứ 3 trở đi' (xem man tailđể biết chi tiết).


-2

Hãy thử làm:

wc -l file_name | tail -n $(awk '{print $1-1}') file_name | sort

3
tôi không hiểu được
Pierre.Vriens
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.