sortuj, ale zachowaj nagłówek na górze

55

Otrzymuję dane wyjściowe z programu, który najpierw tworzy jeden wiersz, który jest wiązką nagłówków kolumn, a następnie wiązką wierszy danych. Chcę wyciąć różne kolumny tego wyjścia i wyświetlić je posortowane według różnych kolumn. Bez nagłówka odcinające i sortowania jest łatwo realizowane poprzez -kopcji sortwraz z cutlub awkaby podzbiór kolumn. Jednak ta metoda sortowania miesza nagłówki kolumn z resztą wierszy danych wyjściowych. Czy istnieje prosty sposób na utrzymanie nagłówków na górze?

Jerry
źródło
1
Natrafiłem na następujący link . Nie mogę jednak uruchomić tej techniki { head -1; sort; }. Zawsze usuwa wiązkę tekstu po pierwszym wierszu. Czy ktoś wie, dlaczego tak się dzieje?
poniedziałek
1
Podejrzewam, że dzieje się tak, ponieważ headwczytuje więcej niż jedną linię do bufora i wyrzuca większość. Mój sedpomysł miał ten sam problem.
Andy
@Jonderry - ta technika działa tylko z lseekmożliwością wprowadzania danych, więc nie będzie działać podczas odczytu z potoku. Działa, jeśli przekierujesz do pliku, >outfilea następnie uruchomisz{ head -n 1; sort; } <outfile
don_crissti

Odpowiedzi:

58

Kradzież pomysłu Andy'ego i uczynienie go funkcją, która jest łatwiejsza w użyciu:

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

Teraz mogę zrobić:

$ 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
źródło
ps -C COMMANDmoże być bardziej odpowiednie niż grep COMMAND, ale to tylko przykład. Nie -Cmożesz także użyć, jeśli użyłeś innej opcji wyboru, takiej jak -U.
Mikel
A może należy to nazwać body? Jak w body sortlub body grep. Myśli?
Mikel
3
Nazwę zmieniono z headerna body, ponieważ wykonujesz akcję na ciele. Mam nadzieję, że to ma większy sens.
Mikel
2
Pamiętajcie, aby wezwać bodywszystkich kolejnych uczestników rurociągu:ps -o pid,comm | body grep less | body sort -k1nr
biskupa
1
@Tim Możesz po prostu napisać <foo body sort -k2lub body sort -k2 <foo. Tylko jedna dodatkowa postać z tego, czego chciałeś.
Mikel
36

Możesz utrzymać nagłówek na górze w następujący sposób dzięki bash:

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

Lub zrób to za pomocą Perla:

command | perl -e 'print scalar (<>); print sort { ... } <>'
Andy
źródło
2
+1 niesamowity. Warto połączyć jako funkcję powłoki.
Mikel
1
+1, z jakiegokolwiek powodu, dla którego preferowana jest podpowłoka lub jest w {}porządku zamiast ()?
poniedziałek
2
IFS=wyłącza dzielenie słów podczas czytania danych wejściowych. Nie uważam, żeby to było konieczne podczas czytania $REPLY. echorozwinie ukośniki odwrotne, jeśli xpg_echojest ustawiony (nie domyślny); printfw takim przypadku jest bezpieczniejszy. echo $REPLYbez cudzysłowów zagęści białe znaki; Myślę, że echo "$REPLY"powinno być dobrze. read -rjest potrzebny, jeśli dane wejściowe mogą zawierać znaki ucieczki odwrotnego ukośnika. Niektóre z nich mogą zależeć od wersji bash.
Andy
1
@Andy: Wow, masz rację, różne zasady dla read REPLY; echo $REPLY(usuwa spacje) i read; echo $REPLY(nie).
Mikel
1
@Andy: IIRC, domyślna wartość xpg_echozależy od twojego systemu, np. W Solarisie Myślę, że domyślnie jest to prawda. Właśnie dlatego Gilles printftak bardzo lubi : to jedyna rzecz o przewidywalnym zachowaniu.
Mikel
23

Znalazłem fajną wersję awk, która działa dobrze w skryptach:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'
Michael Kuhn
źródło
1
Podoba mi się to, ale wymaga trochę wyjaśnienia - potok znajduje się w skrypcie awk. Jak to działa? Czy wywołuje to sortpolecenie zewnętrznie? Czy ktoś zna przynajmniej link do strony wyjaśniającej użycie potoku w awk?
Wildcard
@Wildcard możesz sprawdzić oficjalną stronę podręcznika lub ten podkład .
lapo
4

Hackish, ale skuteczny: poprzedzaj sortowanie 0wszystkimi wierszami nagłówka i 1wszystkimi innymi wierszami. Rozbierz pierwszą postać po sortowaniu.

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-
Gilles „SO- przestań być zły”
źródło
3

Oto niektóre szumy magicznej linii perlowej, przez które można przesyłać dane wyjściowe, aby posortować wszystko, ale pierwszą linię należy trzymać na górze: perl -e 'print scalar <>, sort <>;'

Ryan Thompson
źródło
2

Wypróbowałem command | {head -1; sort; }rozwiązanie i mogę potwierdzić, że to naprawdę psuje rzeczy - headodczytuje z rury wiele wierszy, a następnie wyprowadza tylko pierwszy. Tak więc reszta danych wyjściowych, które head nie zostały odczytane, jest przekazywana do - sortNIE reszty danych wyjściowych począwszy od wiersza 2!

W rezultacie brakuje linii (i jednej linii częściowej!), Które były na początku danych wyjściowych polecenia (z wyjątkiem tego, że nadal masz pierwszą linię) - fakt ten łatwo potwierdzić, dodając potok wcna końcu powyższy rurociąg - ale niezwykle trudno jest go wyśledzić, jeśli nie wiesz! Spędziłem co najmniej 20 minut próbując dowiedzieć się, dlaczego miałem częściową linię (pierwsze 100 bajtów odciętą) w moich wynikach przed jej rozwiązaniem.

Skończyło się na tym, że działało pięknie i nie wymagało dwukrotnego uruchomienia polecenia:

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

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

rm $myfile

Jeśli chcesz umieścić dane wyjściowe w pliku, możesz to zmodyfikować, aby:

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

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

rm $myfile
Dzika karta
źródło
Możesz użyć headwbudowanego ksh93 lub linenarzędzia (w systemach, które wciąż go mają) lub gnu-sed -u qalbo IFS=read -r line; printf '%s\n' "$line", które odczytują wejściowy bajt po jednym, aby tego uniknąć.
Stéphane Chazelas
1

Myślę, że to jest najłatwiejsze.

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

lub ten, który jest prawdopodobnie szybszy, ponieważ nie tworzy sub-powłoki

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

Inne fajne zastosowania

losuj linie po wierszu nagłówka

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

odwróć linie po wierszu nagłówka

cat file.txt |  ( head -n 1 ; tac )
użytkownik2449151
źródło
2
Zobacz unix.stackexchange.com/questions/11856/… . To nie jest dobre rozwiązanie.
Wildcard,
1
Nie działa, cat file | { head -n 1 ; sort ; } > file2pokaż tylko głowę
Peter Krauss
0
command | head -1; command | tail -n +2 | sort
Sarva
źródło
4
To zaczyna się commanddwa razy. Dlatego ogranicza się do niektórych konkretnych poleceń. Jednak dla żądanego pspolecenia w tym przykładzie zadziałałoby.
jofel
0

Prosty i bezpośredni!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> „n” określa numer linii, a „d” oznacza delete.
Jatsui
źródło
1
Tak jak jofel skomentował półtora roku temu na odpowiedź Sarvy, zaczyna się to commanddwa razy. Tak naprawdę nie nadaje się do użycia w rurociągu.
Wildcard,
0

Przybyłem tutaj, szukając rozwiązania dla polecenia w. To polecenie pokazuje szczegółowe informacje o tym, kto jest zalogowany i co robi.

Aby pokazać posortowane wyniki, ale z nagłówkami trzymanymi na górze (są 2 linie nagłówków), zdecydowałem się na:

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

Oczywiście uruchamia to polecenie wdwukrotnie i dlatego może nie być odpowiednie dla wszystkich sytuacji. Jednak na jego korzyść jest znacznie łatwiej zapamiętać.

Zauważ, że tail -n +3oznacza „pokaż wszystkie linie od 3-go roku” (zobacz man tailszczegóły).

Robert
źródło
-2

Spróbuj zrobić:

wc -l file_name | tail -n $(awk '{print $1-1}') file_name | sort
Barry
źródło
3
nie rozumiem
Pierre.Vriens