UNIX sıralamasında başlık satırlarını yok saymanın bir yolu var mı?


102

UNIX (benim durumumda Cygwin) sıralama yardımcı programını kullanarak sıralamaya çalıştığım sabit genişlikli bir alan dosyam var.

Sorun, dosyanın en üstünde iki satırlık bir başlık olması ve dosyanın en altına sıralanmasıdır (her başlık satırı iki nokta üst üste ile başladığından).

Sıralamaya "ilk iki satırı sıralanmamış boyunca geçir" demenin veya iki nokta üst üste satırlarını en üste sıralayan bir sıralama belirtmenin bir yolu var mı - kalan satırlar her zaman 6 basamaklı bir sayısal ile başlar (bu aslında anahtar I yardımcı olacaksa).

Misal:

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

sıralanmalıdır:

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

Kayıt için: Şu ana kadar kullandığım komut satırı "sort -t \\ -k1.1,1.6 <file>" [veriler boşluk içerebilir, ancak hiçbir zaman ters eğik çizgi içermez]
Rob Gilliam

Yanıtlar:


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

Parantezler bir alt kabuk oluşturur ve standart çıktıyı tek bir komuttan geliyormuş gibi yönlendirebilir veya yönlendirebilirsiniz.


Teşekkürler; Bu cevabı en eksiksiz ve özlü göründüğü için kabul ediyorum (ve ne yaptığını anlıyorum!) - yine de "head -n 2" olmalı :-)
Rob Gilliam

1
Teşekkürler, 'baş' kısmını düzelttim.
BobS

4
Bu sürümün aktarılmış veriler üzerinde çalışmasını sağlamanın bir yolu var mı? Denedim tee >(head -n $header_size) | tail -n +$header_size | sort, ancak baş tail|sortborunun arkasından geçiyor gibi görünüyor , bu yüzden başlık sonunda yazdırılıyor. Bu deterministik mi yoksa yarış durumu mu?
Damien Pollet

Muhtemelen catstdin'i geçici bir dosyaya yeniden yönlendirmek için kullandığınız bir şeyi bir araya getirebilir , ardından yukarıdaki komutu bu yeni dosyada çalıştırabilirsiniz, ancak yeterince çirkinleşmeye başlıyor, muhtemelen burada verilen awk tabanlı çözümlerden birini kullanmak daha iyidir. diğer yanıtlar.
BobS

@DamienPollet: See Dave 'in cevabı .
Jonathan Leffler

66

Kullanmanın sakıncası awkyoksa, awkyerleşik boru yeteneklerinden yararlanabilirsiniz.

Örneğin.

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

Bu, ilk iki satırı kelimesi kelimesine yazdırır ve geri kalanını aktarır sort.

Bunun, borulu bir girdinin parçalarını seçici olarak sıralayabilme gibi çok özel bir avantajı olduğunu unutmayın. önerilen diğer tüm yöntemler yalnızca birden çok kez okunabilen düz dosyaları sıralayacaktır. Bu her şeyde işe yarar.


2
Çok güzel ve keyfi borularla çalışıyor, sadece dosyalarla değil!
lapo

4
Güzel, awk beni şaşırtmayı asla bırakmaz. Ayrıca, gerek yok $0, printyeter.
nachocab

1
@SamWatkins freeseek'in cevabı daha az çirkin.
fess.

-R seçeneği sıralamak için ne yapıyor? Bunun ters sıralama mı olması gerekiyor?
gvrocha

32

Borulu veriler üzerinde çalışan bir sürüm:

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

Başlığınızda birden çok satır varsa:

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

Bu çözüm buradan


9
Güzel. Kullandığım tek başlık durumu extract_data | (read h; echo "$h"; sort) için hatırlanacak kadar kısa. Örneğiniz daha uç durumları kapsıyor. :) Bu en iyi cevap. borular üzerinde çalışır. awk yok.
fess.

1
Tamam, bunun üstesinden geldim ve görünüşe göre bash bu işi yapmak için özel uzunluklara gidiyor. Genel olarak, bunu C veya başka bir dilde kodladıysanız, bu işe yaramayacaktır çünkü stdio, ilk başlık satırından daha fazlasını okuyacaktır. Aranabilir bir dosya üzerinde çalıştırırsanız, bash daha büyük bir yığın okur (benim testimde 128 bayt), ardından l, ilk satırın sonundan sonra geri döner. Bir boru üzerinde çalıştırırsanız, bash satırın sonunu geçene kadar her seferinde bir karakter okur.
Sam Watkins

Güzel! Sadece başlığı yemek istiyorsanız, hatırlamak daha da kolay:extract_data | (read; sort)
Jason Suárez

Bu neredeyse mükemmel ama baştaki ve sondaki boşlukları korumak için "okumak" yerine "IFS = oku" kullanmanız gerekiyor.
Stanislav German-Evtushenko

6
Bence bu kabul edilen cevap olmalı. Basit, özlü ve daha esnektir, çünkü borulu veriler üzerinde de çalışır.
Paul I

13

Basit durumlarda, sedişi zarif bir şekilde yapabilir:

    your_script | (sed -u 1q; sort)

Veya eşdeğer olarak,

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

Anahtar 1q- ilk satırda (başlık) yazdır ve çık (girdinin geri kalanını bırakarak sort).

Verilen örnek 2qiçin hile yapacak.

-uAnahtar (tamponsuz) olanlar için gereklidir sedsize geçmesi istediğiniz (özellikle, GNU en) aksi büyük kümeler halinde girdi okurdum böylelikle veri tüketen s sortyerine.


1
Merhaba @Andrea; Stack Overflow'a hoş geldiniz. Korkarım cevabınız işe yaramıyor, en azından Windows'ta Git Bash'de test ettiğimde (6 yıl önce farklı bir iş kullandığım Cygwin'den geçtim). Sed komutu, tüm verileri stdin'den çekerek sıralamak için geçirilecek hiçbir veri bırakmaz. Your_data | (sed 1q; wc -l) ne demek istediğimi görmek için.
Rob Gilliam

1
Bu, girdiyi ikinci kez sed komutuna iletirseniz işe yarayabilir, örneğin: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sıralanmış.csv
Harry Cramer

8

Kullanabilirsiniz tail -n +3 <file> | sort ...(tail dosya içeriğini 3. satırdan çıkaracaktır).


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

misal:

> 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

3

Sadece 2 satır kod alır ...

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

Sayısal veriler için -n gereklidir. Alfa sıralaması için -n gerekli değildir.

Örnek dosya:
$ cat test.txt

başlık
8
5
100
1
-1

Sonuç:
$ cat a.tmp

başlık
-1
1
5
8
100


1
Bu temelde kabul edilen cevapla aynı cevap değil mi? (BobS'un yaklaşımı sonucu stdout'a koyması dışında, sonucu dosyaya yazılmadan önce diğer filtrelerden göndermenize izin verir)
Rob Gilliam

1

İşte argümanların tam olarak sıralama gibi olduğu bir bash işlevi. Destekleyici dosyalar ve borular.

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
}

Nasıl çalışır. Bu satır, en az bir argüman olup olmadığını ve son argümanın bir dosya olup olmadığını kontrol eder.

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

Bu, dosyayı ayrı bir argümana kaydeder. Son tartışmayı silmek üzere olduğumuzdan beri.

        local file=${@: -1}

Burada son argümanı kaldırıyoruz. Çünkü bunu bir tür argüman olarak iletmek istemiyoruz.

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

Son olarak, awk olarak sıralamak için argümanları (dosya buysa son argümanı eksi) ileterek awk bölümünü yaparız. Bu başlangıçta Dave tarafından önerildi ve sıralı argümanlar alacak şekilde değiştirildi. Boruluyorsak $fileboş olacağı gerçeğine güveniyoruz , bu yüzden görmezden gelinir.

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

Virgülle ayrılmış dosya ile örnek kullanım.

$ 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

0

Python ile:

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)

sistemde Python'un kurulu olduğunu varsayar (benimki yok)
Rob Gilliam

0

İşte diğer cevaplardan türetilen bir bash kabuğu işlevi. Hem dosyaları hem de boruları yönetir. İlk argüman dosya adı veya stdin için '-' dir. Kalan bağımsız değişkenler sıralamak için iletilir. Birkaç örnek:

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

Kabuk işlevi:

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
}

0

Bu, Ian Sherbin'in cevabı ile aynı, ancak benim uygulamam: -

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;

-4
cat file_name.txt | sed 1d | sort 

Bu ne istersen yapacak.


1) Bu yalnızca başlık satırını kaldırır ve geri kalanını sıralar, başlığı olduğu gibi bırakarak başlık satırının altındaki her şeyi sıralamaz. 2) başlık aslında iki satır olduğunda yalnızca ilk satırı kaldırır (soruyu okuyun). 3) "sed 1d <dosya_adı.txt" veya hatta sadece "sed 1d dosya_adı.txt" aynı etkiye sahipken neden "kedi dosya_adı.txt | sed 1d" kullanıyorsunuz?
Rob Gilliam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.