C'è un modo per ignorare le righe di intestazione in un ordinamento UNIX?


102

Ho un file con campo a larghezza fissa che sto cercando di ordinare utilizzando l'utilità di ordinamento UNIX (Cygwin, nel mio caso).

Il problema è che c'è un'intestazione di due righe all'inizio del file che viene ordinata in fondo al file (poiché ogni riga di intestazione inizia con due punti).

C'è un modo per dire all'ordinamento di "passare le prime due righe non ordinate" o di specificare un ordine che ordina le due righe in alto - le righe rimanenti iniziano sempre con un numero di 6 cifre (che in realtà è la chiave I Sto smistando) se questo aiuta.

Esempio:

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

dovrebbe ordinare a:

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

Per la cronaca: la riga di comando che sto usando finora è "sort -t \\ -k1.1,1.6 <file>" [i dati possono contenere spazi, ma non conterranno mai una barra rovesciata]
Rob Gilliam

Risposte:


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

Le parentesi creano una subshell, avvolgendo lo stdout in modo da poterlo reindirizzare o reindirizzare come se provenisse da un singolo comando.


Thanks; I'm accepting this answer as it seems most complete and concise (and I understand what it's doing!) - it should be "head -n 2", though :-)
Rob Gilliam

1
Thanks, fixed the 'head' part.
BobS

4
Is there a way to have this version work on piped-in data? I tried with tee >(head -n $header_size) | tail -n +$header_size | sort, but head seems to run after the tail|sort pipe, so the header ends up printed in the end. Is this deterministic or a race condition?
Damien Pollet

Probabilmente potresti mettere insieme qualcosa in cui usi catper reindirizzare lo stdin a un file temporaneo, quindi eseguire il comando sopra su quel nuovo file, ma sta iniziando a diventare abbastanza brutto che probabilmente è meglio usare una delle soluzioni basate su awk fornite in le altre risposte.
BobS

@DamienPollet: vedi la risposta di Dave .
Jonathan Leffler

63

Se non ti dispiace usare awk, puoi sfruttare le awkabilità di pipe integrate di

per esempio.

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

Questo stampa le prime due righe alla lettera e convoglia il resto sort.

Si noti che questo ha il vantaggio molto specifico di essere in grado di ordinare selettivamente parti di un ingresso convogliato. tutti gli altri metodi suggeriti ordinano solo file semplici che possono essere letti più volte. Funziona su qualsiasi cosa.


2
Molto carino e funziona con pipe arbitrarie, non solo file!
lapo

4
Bello, awk non smette mai di sorprendermi. Inoltre, non è necessario $0, printè sufficiente.
nachocab

1
La risposta di @SamWatkins freeseek è meno brutta.
fess.

Cosa fa l'opzione -r per ordinare? Questo dovrebbe essere un ordine inverso?
gvrocha

32

Ecco una versione che funziona sui dati in pipe:

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

Se la tua intestazione ha più righe:

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

Questa soluzione è da qui


9
simpatico. per il caso di intestazione singola che uso extract_data | (read h; echo "$h"; sort) è abbastanza breve da ricordare. il tuo esempio copre più casi limite. :) Questa è la migliore risposta. funziona su tubi. no awk.
fess.

1
Ok, ho scritto questo e sembra che bash faccia di tutto per farlo funzionare. In generale, se lo codifichi in C o in un altro linguaggio, non funzionerebbe perché stdio leggerebbe più della prima riga di intestazione. Se lo esegui su un file ricercabile, bash legge un blocco più grande (128 byte nel mio test), quindi torna alla fine della prima riga. Se lo esegui su una pipe, bash legge un carattere alla volta finché non supera la fine della riga.
Sam Watkins

Bello! Se vuoi solo mangiare il colpo di testa, è ancora più facile da ricordare:extract_data | (read; sort)
Jason Suárez

Questo è quasi perfetto, ma devi usare "IFS = read" invece di "read" per mantenere gli spazi iniziali e finali.
Stanislav German-Evtushenko

6
Questa dovrebbe essere la risposta accettata secondo me. Semplice, conciso e più flessibile in quanto funziona anche su dati in pipe.
Paul I

12

In casi semplici, sedpuò svolgere il lavoro elegantemente:

    your_script | (sed -u 1q; sort)

o equivalentemente,

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

La chiave è in 1q- stampa la prima riga (intestazione) ed esci (lasciando il resto dell'input a sort).

Per l'esempio fornito, 2qfarà il trucco.

L' -uinterruttore (senza buffer) è richiesto per quei messaggi sed(in particolare GNU) che altrimenti leggerebbero l'input in blocchi, consumando così i dati che si desidera sortinvece passare .


1
Ciao, @Andrea; benvenuto in Stack Overflow. Temo che la tua risposta non funzioni, almeno non quando la sto testando in Git Bash su Windows (sono passato da Cygwin, la shell che stavo usando un lavoro diverso 6 anni fa). Il comando sed estrae tutti i dati dallo stdin, senza lasciare dati da passare per l'ordinamento. Prova a cambiare il comando in cat your_data | (sed 1q; wc -l) per vedere cosa intendo.
Rob Gilliam,

1
Questo potrebbe funzionare se passi l'input una seconda volta al comando sed, in questo modo: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> Sort.csv
Harry Cramer

8

Puoi usare tail -n +3 <file> | sort ...(tail emetterà il contenuto del file dalla 3a riga).


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

esempio:

> 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

Ci vogliono solo 2 righe di codice ...

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

Per un dato numerico, è richiesto -n. Per l'ordinamento alfa, -n non è richiesto.

File di esempio:
$ cat test.txt

intestazione
8
5
100
1
-1

Risultato:
$ cat a.tmp

intestazione
-1
1
5
8
100


1
Non è fondamentalmente la stessa risposta della risposta accettata? (Tranne che l'approccio di BobS mette il risultato su stdout, permettendoti di inviare il risultato attraverso altri filtri prima di essere scritto su file, se necessario)
Rob Gilliam

1

Quindi ecco una funzione bash in cui gli argomenti sono esattamente come sort. Supporto di file e pipe.

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
}

Come funziona. Questa riga controlla se c'è almeno un argomento e se l'ultimo argomento è un file.

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

Ciò salva il file in un argomento separato. Visto che stiamo per cancellare l'ultimo argomento.

        local file=${@: -1}

Qui togliamo l'ultimo argomento. Poiché non vogliamo passarlo come argomento di ordinamento.

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

Infine, eseguiamo la parte awk, passando gli argomenti (meno l'ultimo argomento se era il file) da ordinare in awk. Questo è stato suggerito in origine da Dave e modificato per accettare argomenti di ordinamento. Facciamo affidamento sul fatto che $filesarà vuoto se stiamo piping, quindi ignorato.

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

Esempio di utilizzo con un file separato da virgole.

$ 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

Con 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)

presume che il sistema abbia Python installato (il mio no)
Rob Gilliam

0

Ecco una funzione di shell bash derivata dalle altre risposte. Gestisce sia file che pipe. Il primo argomento è il nome del file o "-" per stdin. Gli argomenti rimanenti vengono passati all'ordinamento. Un paio di esempi:

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

La funzione 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
}

0

Questa è la stessa risposta di Ian Sherbin ma la mia implementazione è: -

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 

Questo farà quello che vuoi.


1) Questo rimuove solo la riga di intestazione e ordina il resto, non ordina tutto sotto la riga di intestazione lasciando intatta l'intestazione. 2) rimuove solo la prima riga, quando l'intestazione è in realtà di due righe (leggi la domanda). 3) Perché usi "cat nome_file.txt | sed 1d" quando "sed 1d <nome_file.txt" o anche solo "sed 1d nome_file.txt" ha lo stesso effetto?
Rob Gilliam
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.