Czy istnieje sposób na ignorowanie wierszy nagłówka podczas sortowania w systemie UNIX?

102

Mam plik pola o stałej szerokości, który próbuję posortować za pomocą narzędzia sortowania UNIX (w moim przypadku Cygwin).

Problem polega na tym, że na górze pliku znajduje się dwuwierszowy nagłówek, który jest sortowany do końca pliku (ponieważ każda linia nagłówka zaczyna się od dwukropka).

Czy istnieje sposób, aby powiedzieć sortowi, czy „przekaż pierwsze dwie linie przez nieposortowane”, albo określić kolejność, która sortuje dwukropki do góry - pozostałe wiersze zawsze zaczynają się 6-cyfrową liczbą (która jest właściwie kluczem I sortuję dalej), jeśli to pomoże.

Przykład:

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

powinien sortować do:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00
Rob Gilliam
źródło
Dla przypomnienia: linia poleceń, której używam do tej pory, to „sort -t \\ -k1.1,1.6 <file>” [dane mogą zawierać spacje, ale nigdy nie będą zawierać odwrotnego ukośnika]
Rob Gilliam

Odpowiedzi:

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

Nawiasy tworzą podpowłokę, zawijając standardowe wyjście, dzięki czemu można je potokować lub przekierować tak, jakby pochodziło z pojedynczego polecenia.

BobS
źródło
Dzięki; Przyjmuję tę odpowiedź, ponieważ wydaje się ona najbardziej kompletna i zwięzła (i rozumiem, co robi!) - ale powinno to być „głowa-n 2” :-)
Rob Gilliam
1
Dzięki, naprawiłem część „głowy”.
BobS
4
Czy istnieje sposób, aby ta wersja działała na danych przesyłanych potokiem? Próbowałem z tee >(head -n $header_size) | tail -n +$header_size | sort, ale wydaje się, że głowica biegnie za tail|sortrurą, więc nagłówek zostaje wydrukowany na końcu. Czy jest to deterministyczne, czy stan rasy?
Damien Pollet
Prawdopodobnie mógłbyś catzłożyć coś razem, w którym używasz do przekierowania stdin do pliku tymczasowego, a następnie uruchomić powyższe polecenie na tym nowym pliku, ale zaczyna być na tyle brzydki, że prawdopodobnie lepiej jest użyć jednego z rozwiązań opartych na awk podanych w inne odpowiedzi.
BobS
@DamienPollet: Zobacz Dave „s odpowiedź .
Jonathan Leffler
66

Jeśli nie masz nic przeciwko używaniu awk, możesz skorzystaćawk wbudowanych możliwości potoku

na przykład.

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

Spowoduje to wydrukowanie pierwszych dwóch wierszy dosłownie, a resztę przekierowuje sort .

Należy zauważyć, że ma to bardzo szczególną zaletę polegającą na możliwości selektywnego sortowania części potokowego wejścia. wszystkie inne sugerowane metody posortują tylko zwykłe pliki, które można odczytać wiele razy. To działa na wszystko.

Dave
źródło
2
Bardzo ładny i działa z dowolnymi potokami, nie tylko plikami!
Lapo
4
Piękny, awk nigdy nie przestaje mnie zaskakiwać. Nie potrzebujesz też $0, printwystarczy.
nachocab
1
@SamWatkins freeseek męska odpowiedź jest mniej brzydki.
fess.
Co robi opcja -r do sortowania? Czy to ma być sortowanie odwrotne?
gvrocha
32

Oto wersja, która działa na danych z potoku:

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

Jeśli twój nagłówek ma wiele linii:

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

To rozwiązanie jest stąd

freeseek
źródło
9
miły. w przypadku pojedynczego nagłówka, którego używam extract_data | (read h; echo "$h"; sort) , jest wystarczająco krótki, aby go zapamiętać. Twój przykład obejmuje więcej skrajnych przypadków. :) To najlepsza odpowiedź. działa na rurach. no awk.
fess.
1
Ok, straciłem to i wydaje się, że bash dokłada wszelkich starań, aby to zadziałało. Ogólnie, jeśli zakodowałbyś to w C lub innym języku, to nie zadziała, ponieważ stdio czyta więcej niż tylko pierwszą linię nagłówka. Jeśli uruchomisz go na pliku, który można przeszukiwać, bash czyta większy fragment (w moim teście 128 bajtów), a następnie leszczy się z powrotem do końca pierwszej linii. Jeśli uruchomisz go na potoku, bash czyta po jednym znaku na raz, aż minie koniec wiersza.
Sam Watkins
Miły! Jeśli chcesz tylko zjeść nagłówek, jeszcze łatwiej to zapamiętać:extract_data | (read; sort)
Jason Suárez
Ten jest prawie doskonały, ale musisz użyć „IFS = read” zamiast „read”, aby zachować początkowe i końcowe spacje.
Stanislav German-Evtushenko
6
Moim zdaniem powinna to być akceptowana odpowiedź. Prosty, zwięzły i bardziej elastyczny, ponieważ działa również na danych potokowych.
Paweł I
13

W prostych przypadkach sedpotrafi wykonać pracę elegancko:

    your_script | (sed -u 1q; sort)

lub równoważnie,

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

Klucz znajduje się w 1q- wypisz pierwszą linię (nagłówek) i wyjdź (pozostawiając resztę danych wejściowychsort ).

Na podany przykład 2q wystarczy.

-uPrzełącznik (niebuforowana) jest wymagana dla tych sedS (zwłaszcza, GNU), że byłoby inaczej czytać wejście w kawałki, przez co zużywa dane, które chcesz przejść sortzamiast.

Andrea
źródło
1
Cześć @ Andrea; witamy w Stack Overflow. Obawiam się, że twoja odpowiedź nie działa, przynajmniej nie, gdy testuję ją w Git Bash na Windowsie (przeniosłem się z Cygwin, powłoki, której używałem 6 lat temu). Polecenie sed pobiera wszystkie dane ze standardowego wejścia, nie pozostawiając żadnych danych do przekazania do sortowania. Spróbuj zmienić polecenie na cat your_data | (sed 1q; wc -l), aby zobaczyć, co mam na myśli.
Rob Gilliam
1
Może to zadziałać, jeśli przekażesz dane wejściowe po raz drugi do polecenia sed, na przykład: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sort.csv
Harry Cramer
8

Możesz użyć tail -n +3 <file> | sort ...(tail wyświetli zawartość pliku z trzeciej linii).

Anton Kovalenko
źródło
4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

przykład:

> 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
źródło
3

Zajmuje tylko 2 linie kodu ...

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

W przypadku danych liczbowych wymagane jest -n. W przypadku sortowania alfa opcja -n nie jest wymagana.

Przykładowy plik:
$ cat test.txt

nagłówek
8
5
100
1
-1

Wynik:
$ cat a.tmp

Nagłówek
-1
1
5
8
100

Ian Sherbin
źródło
1
Czy nie jest to w zasadzie ta sama odpowiedź, co zaakceptowana odpowiedź? (Z wyjątkiem podejścia Boba, który umieszcza wynik na stdout, co pozwala na przesłanie wyniku przez inne filtry przed zapisaniem do pliku, jeśli to konieczne)
Rob Gilliam,
1

Oto funkcja bash, w której argumenty są dokładnie takie same, jak sort. Obsługiwane pliki i potoki.

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
}

Jak to działa. Ta linia sprawdza, czy jest co najmniej jeden argument i czy ostatni argument jest plikiem.

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

To zapisuje plik do oddzielnego argumentu. Ponieważ mamy zamiar skasować ostatni argument.

        local file=${@: -1}

Tutaj usuwamy ostatni argument. Ponieważ nie chcemy przekazywać tego jako argumentu sortowania.

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

Na koniec wykonujemy część awk, przekazując argumenty (bez ostatniego argumentu, jeśli był to plik) do sortowania w awk. Zostało to oryginalnie zasugerowane przez Dave'a i zmodyfikowane, aby przyjąć sortowanie argumentów. Polegamy na fakcie, że $filebędzie pusty, jeśli będziemy podłączać rurociąg, a zatem zostaniemy zignorowani.

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

Przykładowe użycie z plikiem oddzielonym przecinkami.

$ 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
grypa
źródło
0

W Pythonie:

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
źródło
zakłada, że ​​system ma zainstalowany Python (mój nie)
Rob Gilliam
0

Oto funkcja powłoki bash pochodząca z innych odpowiedzi. Obsługuje zarówno pliki, jak i potoki. Pierwszym argumentem jest nazwa pliku lub „-” dla standardowego wejścia. Pozostałe argumenty są przekazywane do sortowania. Kilka przykładów:

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

Funkcja powłoki:

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
źródło
0

To jest to samo, co odpowiedź Iana Sherbina, ale moja realizacja to: -

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
źródło
-4
cat file_name.txt | sed 1d | sort 

To zrobi, co chcesz.

Sathish G.
źródło
1) To tylko usuwa linię nagłówka i sortuje resztę, nie sortuje wszystkiego poniżej linii nagłówka, pozostawiając nagłówek nienaruszony. 2) usuwa tylko pierwszą linię, gdy nagłówek składa się w rzeczywistości z dwóch linii (przeczytaj pytanie). 3) Dlaczego używasz „cat nazwa_pliku.txt | sed 1d”, kiedy „sed 1d <nazwa_pliku.txt” lub nawet „sed 1d nazwa_pliku.txt” daje taki sam efekt?
Rob Gilliam