ordina ma mantieni la riga di intestazione in alto


56

Sto ottenendo output da un programma che dapprima produce una riga che è un gruppo di intestazioni di colonna, quindi un gruppo di righe di dati. Voglio tagliare varie colonne di questo output e visualizzarlo in base a varie colonne. Senza le intestazioni, il taglio e l'ordinamento vengono facilmente eseguiti tramite l' -kopzione per sortinsieme cuto awkper visualizzare un sottoinsieme delle colonne. Tuttavia, questo metodo di ordinamento mescola le intestazioni di colonna con il resto delle righe di output. C'è un modo semplice per mantenere le intestazioni in alto?


1
Mi sono imbattuto nel seguente link . Tuttavia, non riesco a far funzionare questa tecnica { head -1; sort; }. Elimina sempre un sacco di testo dopo la prima riga. Qualcuno sa perché questo accade?
jonderry,

1
Sospetto sia perché headsta leggendo più di una riga in un buffer e sta gettando via gran parte di essa. La mia sedidea ha avuto lo stesso problema.
Andy,

@jonderry - quella tecnica funziona solo con lseekinput in grado quindi non funzionerà durante la lettura da una pipe. >outfile{ head -n 1; sort; } <outfile
Funzionerà

Risposte:


58

Rubare l'idea di Andy e renderla una funzione in modo che sia più facile da usare:

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

Ora posso fare:

$ 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

ps -C COMMANDpotrebbe essere più appropriato di grep COMMAND, ma è solo un esempio. Inoltre, non puoi usare -Cse hai usato anche un'altra opzione di selezione come -U.
Mikel,

O forse dovrebbe essere chiamato body? Come in body sorto body grep. Pensieri?
Mikel,

3
Rinominato da headera body, perché stai facendo l'azione sul corpo. Spero che abbia più senso.
Mikel,

2
Ricorda di chiamare bodytutti i successivi partecipanti alla pipeline:ps -o pid,comm | body grep less | body sort -k1nr
vescovo

1
@Tim Puoi semplicemente scrivere <foo body sort -k2o body sort -k2 <foo. Solo un personaggio in più da quello che volevi.
Mikel,

37

Puoi mantenere l'intestazione in alto in questo modo con bash:

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

O fallo con perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'

2
+1 fantastico. Vale la pena raggruppare come funzione shell penso.
Mikel,

1
+1, qualsiasi motivo per cui è preferibile una subshell o va {}bene invece di ()?
jonderry,

2
IFS=disabilita la divisione delle parole durante la lettura dell'input. Non penso sia necessario quando si legge $REPLY. echoespande la barra rovesciata se xpg_echoè impostata (non l'impostazione predefinita); printfè più sicuro in quel caso. echo $REPLYsenza virgolette si condenserà lo spazio bianco; Penso che echo "$REPLY"dovrebbe andare bene. read -rè necessario se l'input può contenere backslash escape. Alcuni di questi potrebbero dipendere dalla versione bash.
Andy,

1
@Andy: Wow, hai ragione, regole diverse per read REPLY; echo $REPLY(elimina gli spazi iniziali ) e read; echo $REPLY(no).
Mikel,

1
@Andy: IIRC, il valore predefinito di xpg_echodipende dal sistema in uso, ad esempio su Solaris penso che sia impostato su true. Ecco perché a Gilles piace printfcosì tanto: è l'unica cosa con un comportamento prevedibile.
Mikel,

23

Ho trovato una bella versione awk che funziona bene negli script:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'

1
Mi piace questo, ma richiede un po 'di spiegazione: la pipe è all'interno dello script awk. Come funziona? Sta chiamando il sortcomando esternamente? Qualcuno conosce almeno un link a una pagina che spiega l'uso di pipe in awk?
Wildcard il

@Wildcard è possibile controllare la pagina del manuale ufficiale o questo primer .
lapo,

4

Hackish ma efficace: anteponi 0a tutte le righe di intestazione e 1a tutte le altre righe prima dell'ordinamento. Spoglia il primo carattere dopo l'ordinamento.

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

3

Ecco alcuni rumori magici della linea perl che puoi convogliare il tuo output per ordinare tutto ma mantenere la prima riga in alto: perl -e 'print scalar <>, sort <>;'


2

Ho provato la command | {head -1; sort; }soluzione e posso confermare che rovina davvero le cose : headlegge in più righe dalla pipe, quindi emette solo la prima. Quindi il resto dell'output, che head non ha letto, viene passato a - sortNON il resto dell'output a partire dalla riga 2!

Il risultato è che mancano le righe (e una riga parziale!) Che erano all'inizio dell'output del comando (tranne che hai ancora la prima riga) - un fatto che è facile da confermare aggiungendo una pipe alla wcfine di la pipeline sopra - ma è straordinariamente difficile da rintracciare se non lo sai! Ho impiegato almeno 20 minuti a cercare di capire perché avevo una linea parziale (i primi 100 byte circa) nel mio output prima di risolverlo.

Quello che ho finito per fare, che ha funzionato magnificamente e non ha richiesto di eseguire il comando due volte, è stato:

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

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

rm $myfile

Se è necessario inserire l'output in un file, è possibile modificarlo in:

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

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

rm $myfile

Puoi usare il headbuilt-in di ksh93 o l' lineutility (su sistemi che ne hanno ancora uno) oppure gnu-sed -u qoppure IFS=read -r line; printf '%s\n' "$line", che leggono l'input di un byte alla volta per evitarlo.
Stéphane Chazelas,

1

Penso che sia più semplice.

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

o questo che è probabilmente più veloce in quanto non crea una sub shell

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

Altri usi interessanti

riordina le righe dopo la riga di intestazione

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

righe inverse dopo la riga di intestazione

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

2
Vedi unix.stackexchange.com/questions/11856/… . Questa non è in realtà una buona soluzione.
Wildcard il

1
Non funziona, cat file | { head -n 1 ; sort ; } > file2mostra solo la testa
Peter Krauss,

0
command | head -1; command | tail -n +2 | sort

4
Questo inizia commanddue volte. Pertanto è limitato ad alcuni comandi specifici. Tuttavia, per il pscomando richiesto nell'esempio, funzionerebbe.
jofel,

0

Semplice e diretto!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' specifica il numero di riga e 'd' indica l'eliminazione.

1
Proprio come ha commentato jofel un anno e mezzo fa sulla risposta di Sarva, questo inizia commanddue volte. Quindi non adatto per l'uso in una pipeline.
Wildcard il

0

Sono venuto qui alla ricerca di una soluzione per il comando w. Questo comando mostra i dettagli di chi ha effettuato l'accesso e cosa stanno facendo.

Per mostrare i risultati ordinati, ma con le intestazioni mantenute in alto (ci sono 2 righe di intestazioni), ho optato per:

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

Ovviamente questo esegue il comando wdue volte e quindi potrebbe non essere adatto a tutte le situazioni. Tuttavia, a suo vantaggio, è sostanzialmente più facile da ricordare.

Si noti che il tail -n +3mezzo "mostra tutte le righe dal 3 ° in poi" (vedere man tailper i dettagli).


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.