Prova di ordinare su due campi, il secondo e il primo


106

Sto cercando di ordinare su più colonne. I risultati non sono quelli previsti.

Ecco i miei dati (people.txt):

Simon Strange 62
Pete Brown 37
Mark Brown 46
Stefan Heinz 52
Tony Bedford 50
John Strange 51
Fred Bloggs 22
James Bedford 21
Emily Bedford 18
Ana Villamor 44
Alice Villamor 50
Francis Chepstow 56

Di seguito funziona correttamente:

bash-3.2$ sort -k2 -k3 <people.txt                                                                                                                    
Emily Bedford 18                                                                                                                                      
James Bedford 21                                                                                                                                      
Tony Bedford 50                                                                                                                                       
Fred Bloggs 22                                                                                                                                        
Pete Brown 37                                                                                                                                         
Mark Brown 46                                                                                                                                         
Francis Chepstow 56                                                                                                                                   
Stefan Heinz 52                                                                                                                                       
John Strange 51                                                                                                                                       
Simon Strange 62                                                                                                                                      
Ana Villamor 44                                                                                                                                       
Alice Villamor 50

Tuttavia, quanto segue non funziona come previsto:

bash-3.2$ sort -k2 -k1 <people.txt                                        
Emily Bedford 18                                                                                                                                      
James Bedford 21                                                                                                                                      
Tony Bedford 50                                                                                                                                       
Fred Bloggs 22                                                                                                                                        
Pete Brown 37                                                                                                                                         
Mark Brown 46                                                                                                                                         
Francis Chepstow 56                                                                                                                                   
Stefan Heinz 52                                                                                                                                       
John Strange 51                                                                                                                                       
Simon Strange 62                                                                                                                                      
Ana Villamor 44                                                                                                                                       
Alice Villamor 50

Stavo cercando di ordinare per cognome e poi per nome, ma vedrai che i Villamors non sono nell'ordine corretto. Speravo di ordinare per cognome, e poi quando i cognomi corrispondevano, per ordinare per nome.

Sembra che ci sia qualcosa su come dovrebbe funzionare, non capisco. Potrei farlo in un altro modo ovviamente (usando awk), ma voglio capire l'ordinamento.

Sto usando la shell Bash standard su Mac OS X.

Risposte:


159

Una specifica chiave come -k2significa prendere in considerazione tutti i campi da 2 alla fine della riga. Quindi Villamor 44finisce prima Villamor 50. Poiché questi due non sono uguali, il primo confronto sort -k2 -k1è sufficiente per discriminare queste due righe e la seconda chiave di ordinamento -k1non viene invocata. Se i due Villamori avessero avuto la stessa età, li -k1avrebbe fatti ordinare per nome.

Per ordinare in base a una singola colonna, utilizzare -k2,2come specifica della chiave. Ciò significa utilizzare i campi da # 2 a # 2, ovvero solo il secondo campo.

sort -k2 -k3 <people.txtè ridondante: è equivalente a sort -k2 <people.txt. Per ordinare per cognome, quindi nome, quindi età, eseguire il comando seguente:

sort -k2,2 -k1,1 <people.txt

o equivalentemente sort -k2,2 -k1 <people.txtpoiché ci sono solo questi tre campi e i separatori sono uguali. In effetti, otterrai lo stesso effetto da sort -k2,2 <people.txt, perché sortusa l'intera linea come ultima risorsa quando tutte le chiavi in ​​un sottoinsieme di linee sono identiche.

Si noti inoltre che il separatore di campo predefinito è la transizione tra uno non vuoto e uno vuoto, quindi le chiavi includeranno gli spazi vuoti iniziali (nell'esempio, per la prima riga, sarà la prima chiave "Emily", ma la seconda chiave " Bedford". -bopzione per eliminare quegli spazi vuoti:

sort -b -k2,2 -k1,1

Può anche essere fatto in base alla chiave aggiungendo il bflag alla fine della specifica di inizio chiave:

sort -k2b,2 -k1,1 <people.txt

Ma qualcosa da tenere a mente: non appena aggiungi una di queste bandiere alla specifica chiave, le bandiere globali (come -n, -r...) non si applicano più a loro, quindi è meglio evitare di mescolare bandiere per chiave e bandiere globali.


6
L'hai inchiodato. Avevo assunto (una cosa pericolosa da fare) che specificare -k1 avrebbe significato usare il campo 1, dove il campo termina con il separatore di campo predefinito (spazio). Ma come hai chiaramente sottolineato, l'opzione k si aspetta che tu specifichi i punti di inizio e fine della chiave, che possono essere o meno un singolo campo. La tua soluzione funziona perfettamente e, cosa più importante, sono chiaro sul perché lo faccia. Grazie molto.
Harry,

Questo è ENORME. Tante altre fonti su KEYDEF parlano di -k1 -k2 senza sottolineare l'importanza del COMMA nel formato per limitare quali colonne sono considerate in ogni fase di ordinamento. Sono stato bloccato su questo per ore fino a quando ho trovato questa risposta. E la pagina man è confusa qui. Non spiega che le posizioni "start and stop" sono specificate con la notazione virgola. Grazie!
Jason Rohrer,

16

Con GNU sortlo fai in questo modo, non sei sicuro di MacOS:

sort -k2,2 -k1 <people.txt

Aggiornamento secondo il commento. Citato da man sort:

   -k, --key=KEYDEF
          sort via a key; KEYDEF gives location and type

   KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where
   F is a field number and C a character position in the field; both are
   origin 1, and the stop position defaults to the line's end.

4
Potresti spiegare questa strana notazione per favore?
scai,

1
Questo mi ha fatto pensare sulla retta linea - grazie per quello. Ma non è necessario specificare il punto di arresto per il secondo -k. Questo è -k2,2 -k1,1 altrimenti il ​​punto di arresto viene preso come fine della linea?
Harry,

@TonyBedford, corretto. Ma non specificare la posizione di arresto non cambierà il risultato per il tuo input attuale, ma forzerà la coerenza nel caso in cui tu abbia mai più linee con identici campi 2 e 1. Quindi preferisco consentire all'ultimo -kdi includere il più possibile.
arte

1
@manatwork Non dovrebbe essere necessario; se tutti i campi specificati sono uguali, sortconfronta l'intera riga. O con GNU sortè possibile utilizzare -sper l'ordinamento stabile.
agosto
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.