Apakah ada cara untuk mengabaikan baris header dalam jenis UNIX?

102

Saya memiliki file fixed-width-field yang saya coba sortir menggunakan utilitas sortir UNIX (Cygwin, dalam kasus saya).

Masalahnya adalah ada dua baris header di bagian atas file yang sedang diurutkan ke bagian bawah file (karena setiap baris header dimulai dengan titik dua).

Apakah ada cara untuk mengetahui pengurutan "lewati dua baris pertama melintasi tanpa urutan" atau untuk menentukan urutan yang mengurutkan garis titik dua ke atas - baris yang tersisa selalu dimulai dengan angka 6 digit (yang sebenarnya adalah kunci I sedang menyortir) jika itu membantu.

Contoh:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
500123TSTMY_RADAR00
222334NOTALINEOUT01
477821USASHUTTLES21
325611LVEANOTHERS00

harus diurutkan ke:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00
Rob Gilliam
sumber
Sebagai catatan: baris perintah yang saya gunakan sejauh ini adalah "sort -t \\ -k1.1,1.6 <file>" [data dapat berisi spasi, tetapi tidak akan pernah berisi garis miring terbalik]
Rob Gilliam

Jawaban:

126
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

Tanda kurung membuat subkulit, membungkus stdout sehingga Anda dapat menyalurkannya atau mengalihkannya seolah-olah itu berasal dari satu perintah.

BobS
sumber
Terima kasih; Saya menerima jawaban ini karena tampaknya paling lengkap dan ringkas (dan saya mengerti apa yang dilakukannya!) - seharusnya "head -n 2", meskipun :-)
Rob Gilliam
1
Terima kasih, perbaiki bagian 'kepala'.
BobS
4
Adakah cara agar versi ini berfungsi pada data yang disalurkan? Saya mencoba dengan tee >(head -n $header_size) | tail -n +$header_size | sort, tetapi kepala tampaknya berjalan setelah tail|sortpipa, jadi header akhirnya dicetak pada akhirnya. Apakah ini deterministik atau kondisi ras?
Damien Pollet
Anda mungkin dapat mengumpulkan sesuatu yang Anda gunakan catuntuk mengarahkan stdin ke file sementara, lalu menjalankan perintah di atas pada file baru itu, tetapi itu mulai menjadi cukup jelek sehingga mungkin lebih baik menggunakan salah satu solusi berbasis awk yang diberikan di tanggapan lainnya.
BobS
@DamienPollet: Lihat Dave 's jawaban .
Jonathan Leffler
63

Jika Anda tidak keberatan menggunakannya awk, Anda dapat memanfaatkan awkkemampuan pipa bawaan

misalnya.

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

Ini mencetak dua baris pertama secara verbatim dan menyalurkan sisanya melalui sort.

Perhatikan bahwa ini memiliki keuntungan yang sangat spesifik karena dapat secara selektif mengurutkan bagian dari input yang disalurkan. semua metode lain yang disarankan hanya akan mengurutkan file biasa yang dapat dibaca berkali-kali. Ini bekerja pada apa saja.

Dave
sumber
2
Sangat bagus, dan berfungsi dengan pipa sembarangan, tidak hanya file!
lapo
4
Cantik, awk tidak pernah berhenti mengejutkanku. Juga, Anda tidak perlu $0, printsudah cukup.
nachocab
1
@SamWatkins freeseek ini jawabannya kurang jelek.
fess.
Apa yang dilakukan opsi -r untuk mengurutkan? Apakah ini seharusnya jenis terbalik?
gvrocha
32

Berikut adalah versi yang berfungsi pada data beralur:

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

Jika tajuk Anda memiliki banyak baris:

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

Solusi ini dari sini

freeseek
sumber
9
bagus. untuk kasus tajuk tunggal yang saya gunakan extract_data | (read h; echo "$h"; sort) cukup pendek untuk diingat. contoh Anda mencakup lebih banyak kasus tepi. :) Ini adalah jawaban terbaik. bekerja di pipa. tidak awk.
fess.
1
Oke, saya mengikat ini dan tampaknya bash bekerja terlalu keras untuk membuat ini berfungsi. Secara umum, jika Anda mengkodekan ini dalam C atau bahasa lain itu tidak akan berhasil karena stdio akan membaca lebih dari sekedar baris header pertama. Jika Anda menjalankannya pada file yang dapat dicari, bash membaca potongan yang lebih besar (128 byte dalam pengujian saya), lalu cari kembali ke akhir baris pertama. Jika Anda menjalankannya pada pipa, bash membaca satu karakter pada satu waktu sampai melewati akhir baris.
Sam Watkins
Bagus! Jika Anda hanya ingin makan sundulan, itu lebih mudah untuk diingat:extract_data | (read; sort)
Jason Suárez
Yang satu ini hampir sempurna tetapi Anda perlu menggunakan "IFS = read" daripada "read" untuk menjaga spasi di depan dan di belakangnya.
Stanislav German-Evtushenko
6
Ini harus menjadi jawaban yang diterima menurut saya. Sederhana, ringkas, dan lebih fleksibel karena juga berfungsi pada data yang disalurkan.
Paul I
12

Dalam kasus sederhana, seddapat melakukan pekerjaan dengan elegan:

    your_script | (sed -u 1q; sort)

atau setara,

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

Kuncinya ada di 1q- cetak baris pertama (header) dan keluar (biarkan sisa masukan ke sort).

Untuk contoh yang diberikan, 2qakan melakukan triknya.

The -uswitch (unbuffered) diperlukan untuk mereka seds (terutama, GNU) yang lain akan membaca masukan dalam potongan, sehingga mengkonsumsi data yang Anda ingin pergi melalui sortsebagai gantinya.

Andrea
sumber
1
Hai, @Andrea; selamat datang di Stack Overflow. Saya khawatir jawaban Anda tidak berfungsi, setidaknya tidak saat saya mengujinya di Git Bash pada Windows (saya telah pindah dari Cygwin, shell yang saya gunakan untuk pekerjaan yang berbeda 6 tahun yang lalu). Perintah sed menarik semua data dari stdin, tidak menyisakan data untuk disortir. Coba ubah perintah menjadi cat your_data | (sed 1q; wc -l) untuk melihat apa yang saya maksud.
Rob Gilliam
1
Ini bisa berfungsi jika Anda meneruskan input untuk kedua kalinya ke perintah sed, seperti ini: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sort.csv
Harry Cramer
8

Anda dapat menggunakan tail -n +3 <file> | sort ...(tail akan menampilkan konten file dari baris ke-3).

Anton Kovalenko
sumber
4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

contoh:

> 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
Vijay
sumber
3

Hanya membutuhkan 2 baris kode ...

head -1 test.txt > a.tmp; 
tail -n+2 test.txt | sort -n >> a.tmp;

Untuk data numerik, -n diperlukan. Untuk pengurutan alfa, -n tidak diperlukan.

File contoh:
$ cat test.txt

tajuk
8
5
100
1
-1

Hasil:
$ cat a.tmp

sundulan
-1
1
5
8
100

Ian Sherbin
sumber
1
Bukankah ini pada dasarnya jawaban yang sama dengan jawaban yang diterima? (Kecuali pendekatan BobS menempatkan hasil pada stdout, memungkinkan Anda untuk mengirim hasil melalui filter lain sebelum ditulis ke file, jika perlu)
Rob Gilliam
1

Jadi, inilah fungsi bash di mana argumen persis seperti sort. Mendukung file dan pipa.

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
}

Bagaimana itu bekerja. Baris ini memeriksa apakah ada setidaknya satu argumen dan jika argumen terakhir adalah file.

    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then

Ini menyimpan file ke argumen terpisah. Karena kami akan menghapus argumen terakhir.

        local file=${@: -1}

Di sini kami menghapus argumen terakhir. Karena kami tidak ingin menyebarkannya sebagai argumen penyortiran.

        set -- "${@:1:$(($#-1))}"

Akhirnya, kami melakukan bagian awk, meneruskan argumen (minus argumen terakhir jika itu adalah file) untuk mengurutkan dalam awk. Ini secara orignally disarankan oleh Dave, dan dimodifikasi untuk mengambil argumen semacam itu. Kami mengandalkan fakta bahwa $fileakan kosong jika kami sedang menyalurkan, sehingga diabaikan.

    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file

Contoh penggunaan dengan file yang dipisahkan koma.

$ 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
flu
sumber
0

Dengan 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)
crusaderky
sumber
pra-anggap sistem telah menginstal Python (milik saya tidak)
Rob Gilliam
0

Berikut adalah fungsi bash shell yang diturunkan dari jawaban lainnya. Ini menangani file dan pipa. Argumen pertama adalah nama file atau '-' untuk stdin. Argumen yang tersisa diteruskan untuk mengurutkan. Beberapa contoh:

$ hsort myfile.txt
$ head -n 100 myfile.txt | hsort -
$ hsort myfile.txt -k 2,2 | head -n 20 | hsort - -r

Fungsi 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
}
JonDeg
sumber
0

Ini sama dengan jawaban Ian Sherbin tetapi penerapan saya adalah: -

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;
Bik
sumber
-4
cat file_name.txt | sed 1d | sort 

Ini akan melakukan apa yang Anda inginkan.

Sathish G
sumber
1) Ini hanya menghapus baris header dan mengurutkan sisanya, tidak menyortir semua yang ada di bawah baris header sehingga header tetap utuh. 2) menghapus baris pertama saja, ketika header sebenarnya adalah dua baris (baca pertanyaannya). 3) Mengapa Anda menggunakan "cat file_name.txt | sed 1d" ketika "sed 1d <file_name.txt" atau bahkan hanya "sed 1d file_name.txt" memiliki efek yang sama?
Rob Gilliam