urutkan tetapi pertahankan baris tajuk di bagian atas

56

Saya mendapatkan output dari program yang pertama kali menghasilkan satu baris yang merupakan sekelompok tajuk kolom, dan kemudian sekelompok baris data. Saya ingin memotong berbagai kolom dari output ini dan melihatnya diurutkan berdasarkan berbagai kolom. Tanpa tajuk, pemotongan dan pemilahan mudah dilakukan melalui -kopsi untuk sortbersama dengan cutatau awkuntuk melihat bagian dari kolom. Namun, metode penyortiran ini mencampur header kolom dengan sisa garis output. Apakah ada cara mudah untuk menjaga header di atas?

jonderry
sumber
1
Saya menemukan tautan berikut . Namun, saya tidak bisa mendapatkan teknik ini { head -1; sort; }untuk bekerja. Itu selalu menghapus banyak teks setelah baris pertama. Apakah ada yang tahu mengapa hal ini terjadi?
jonderry
1
Saya curiga itu karena headmembaca lebih dari satu baris ke buffer dan membuang sebagian besar darinya. sedIde saya punya masalah yang sama.
Andy
@jonderry - teknik itu hanya bekerja dengan lseekinput yang dapat sehingga tidak akan berfungsi saat membaca dari sebuah pipa. Ini akan berfungsi jika Anda mengarahkan ulang ke file >outfiledan kemudian menjalankan{ head -n 1; sort; } <outfile
don_crissti

Jawaban:

58

Mencuri ide Andy dan menjadikannya fungsi agar lebih mudah digunakan:

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

Sekarang saya bisa melakukan:

$ 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
Mikel
sumber
ps -C COMMANDmungkin lebih tepat daripada grep COMMAND, tapi itu hanya sebuah contoh. Selain itu, Anda tidak dapat menggunakan -Cjika Anda juga menggunakan opsi pemilihan lain seperti -U.
Mikel
Atau mungkin itu harus disebut body? Seperti dalam body sortatau body grep. Pikiran?
Mikel
3
Berganti nama dari headermenjadi body, karena Anda melakukan aksi pada tubuh. Semoga itu lebih masuk akal.
Mikel
2
Ingatlah untuk memanggil bodysemua peserta saluran pipa berikut:ps -o pid,comm | body grep less | body sort -k1nr
Uskup
1
@Tim Anda cukup menulis <foo body sort -k2atau body sort -k2 <foo. Hanya satu karakter tambahan dari apa yang Anda inginkan.
Mikel
37

Anda dapat menjaga tajuk di bagian atas seperti ini dengan bash:

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

Atau lakukan dengan perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'
Andy
sumber
2
+1 mengagumkan. Layak dibundel sebagai fungsi shell saya pikir.
Mikel
1
+1, alasan apa pun mengapa subshell lebih disukai, atau {}ok ()?
jonderry
2
IFS=menonaktifkan pemisahan kata saat membaca input. Saya pikir itu tidak perlu ketika membaca $REPLY. echoakan memperluas backslash lolos jika xpg_echodiatur (bukan default); printflebih aman dalam hal itu. echo $REPLYtanpa kutipan akan memadatkan spasi putih; Saya pikir echo "$REPLY"harus baik-baik saja. read -rdiperlukan jika input mungkin berisi garis miring terbalik. Beberapa dari ini mungkin tergantung pada versi bash.
Andy
1
@Andy: Wow, Anda benar, aturan berbeda untuk read REPLY; echo $REPLY(strip memimpin ruang) dan read; echo $REPLY(tidak).
Mikel
1
@Andy: IIRC, nilai default xpg_echotergantung pada sistem Anda, misalnya pada Solaris Saya pikir itu default ke true. Inilah mengapa Gilles printfsangat menyukai : itu satu-satunya hal dengan perilaku yang dapat diprediksi.
Mikel
23

Saya menemukan versi awk yang bagus yang bekerja dengan baik dalam skrip:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'
Michael Kuhn
sumber
1
Saya suka ini, tetapi membutuhkan sedikit penjelasan - pipa ada di dalam skrip awk. Bagaimana cara kerjanya? Apakah itu memanggil sortperintah secara eksternal? Apakah ada yang tahu setidaknya tautan ke halaman yang menjelaskan penggunaan pipa dalam awk?
Wildcard
@Wildcard Anda dapat memeriksa halaman manual resmi atau primer ini .
Lapo
4

Meretas tetapi efektif: tambahkan terlebih dahulu 0ke semua baris tajuk dan 1ke semua baris lain sebelum mengurutkan. Strip karakter pertama setelah mengurutkan.

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

Inilah beberapa noise perl line ajaib yang dapat Anda gunakan untuk mengurutkan semuanya untuk mengurutkan semuanya tetapi pertahankan baris pertama di atas: perl -e 'print scalar <>, sort <>;'

Ryan Thompson
sumber
2

Saya mencoba command | {head -1; sort; }solusinya dan dapat mengonfirmasi bahwa itu benar-benar mengacaukan segalanya - headdibaca dalam beberapa baris dari pipa, kemudian hanya keluaran yang pertama. Jadi sisa dari output, yang head tidak membaca, diteruskan ke - sortTIDAK sisa output mulai dari baris 2!

Hasilnya adalah bahwa Anda kehilangan baris (dan satu baris parsial!) Yang ada di awal output perintah Anda (kecuali Anda masih memiliki baris pertama) - fakta yang mudah untuk dikonfirmasi dengan menambahkan pipa ke wcpada akhir pipa di atas - tetapi itu sangat sulit dilacak jika Anda tidak tahu ini! Saya menghabiskan setidaknya 20 menit mencoba mencari tahu mengapa saya memiliki garis parsial (100 byte pertama atau lebih terputus) dalam output saya sebelum menyelesaikannya.

Apa yang akhirnya saya lakukan, yang bekerja dengan indah dan tidak perlu menjalankan perintah dua kali, adalah:

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

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

rm $myfile

Jika Anda perlu memasukkan output ke file, Anda dapat memodifikasi ini ke:

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

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

rm $myfile
Wildcard
sumber
Anda dapat menggunakan headbuiltin ksh93 atau lineutilitas (pada sistem yang masih memiliki satu) atau gnu-sed -u qatau IFS=read -r line; printf '%s\n' "$line", yang membaca input satu byte pada satu waktu untuk menghindari itu.
Stéphane Chazelas
1

Saya pikir ini paling mudah.

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

atau ini yang mungkin lebih cepat karena tidak membuat sub shell

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

Penggunaan keren lainnya

garis acak setelah baris tajuk

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

garis terbalik setelah baris tajuk

cat file.txt |  ( head -n 1 ; tac )
pengguna2449151
sumber
2
Lihat unix.stackexchange.com/questions/11856/… . Ini sebenarnya bukan solusi yang baik.
Wildcard
1
Tidak bekerja, cat file | { head -n 1 ; sort ; } > file2hanya kepala pertunjukan
Peter Krauss
0
command | head -1; command | tail -n +2 | sort
Sarva
sumber
4
Ini dimulai commanddua kali. Karena itu terbatas pada beberapa perintah tertentu. Namun, untuk psperintah yang diminta dalam contoh, itu akan berhasil.
jofel
0

Sederhana dan mudah!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' menentukan baris no., dan 'd' adalah singkatan dari delete.
Jatsui
sumber
1
Sama seperti komentar jofel satu setengah tahun yang lalu tentang jawaban Sarva, ini dimulai commanddua kali. Jadi tidak benar-benar cocok untuk digunakan dalam saluran pipa.
Wildcard
0

Saya datang ke sini mencari solusi untuk perintah itu w. Perintah ini menunjukkan detail siapa yang masuk dan apa yang mereka lakukan.

Untuk menunjukkan hasil yang diurutkan, tetapi dengan header tetap di atas (ada 2 baris header), saya memutuskan:

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

Jelas ini menjalankan perintah wdua kali dan karenanya mungkin tidak cocok untuk semua situasi. Namun, untuk keuntungannya secara substansial lebih mudah diingat.

Perhatikan bahwa tail -n +3sarana 'tunjukkan semua garis dari tanggal 3 dan seterusnya' (lihat man tailuntuk perincian).

Robert
sumber
-2

Coba lakukan:

wc -l file_name | tail -n $(awk '{print $1-1}') file_name | sort
Barry
sumber
3
saya tidak mengerti
Pierre.Vriens