Tratando de ordenar en dos campos, segundo y luego primero


106

Estoy tratando de ordenar en varias columnas. Los resultados no son los esperados.

Aquí están mis datos (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

Lo siguiente funciona correctamente:

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

Pero, lo siguiente no funciona como se esperaba:

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

Intenté ordenar por apellido y luego por nombre, pero verá que los Villamors no están en el orden correcto. Esperaba ordenar por apellido, y luego, cuando los apellidos coincidían, ordenar por nombre.

Parece que hay algo sobre cómo debería funcionar esto, no lo entiendo. Podría hacer esto de otra manera, por supuesto (usando awk), pero quiero entender el tipo.

Estoy usando el shell Bash estándar en Mac OS X.

Respuestas:


159

Una especificación clave como -k2significa tener en cuenta todos los campos desde 2 hasta el final de la línea. Así Villamor 44termina antes Villamor 50. Como estos dos no son iguales, la primera comparación sort -k2 -k1es suficiente para discriminar estas dos líneas y -k1no se invoca la segunda clave de clasificación . Si los dos Villamors hubieran tenido la misma edad, los -k1habría ordenado por nombre.

Para ordenar por una sola columna, use -k2,2como la especificación clave. Esto significa utilizar los campos del # 2 al # 2, es decir, solo el segundo campo.

sort -k2 -k3 <people.txtes redundante: es equivalente a sort -k2 <people.txt. Para ordenar por apellidos, luego nombres, luego edad, ejecute el siguiente comando:

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

o equivalente, sort -k2,2 -k1 <people.txtya que solo hay estos tres campos y los separadores son los mismos. De hecho, obtendrá el mismo efecto sort -k2,2 <people.txt, ya que sortutiliza toda la línea como último recurso cuando todas las teclas en un subconjunto de líneas son idénticas.

También tenga en cuenta que el separador de campo predeterminado es la transición entre un espacio en blanco y otro en blanco, por lo que las claves incluirán los espacios en blanco iniciales (en su ejemplo, para la primera línea, la primera clave será "Emily", pero la segunda clave) " Bedford". -bopción para quitar esos espacios en blanco:

sort -b -k2,2 -k1,1

También se puede hacer por clave agregando el bindicador al final de la especificación de inicio de clave:

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

Sin embargo, algo a tener en cuenta: en cuanto se agrega un tal indicador para la especificación de clave, los indicadores globales (como -n, -r...) ya no se aplican a ellos por lo que es mejor para evitar la mezcla per-clave banderas y banderas globales.


66
Lo lograste. Supuse (algo peligroso que hacer) que especificar -k1 significaría usar el campo 1, donde el campo termina en el separador de campo predeterminado (espacio). Pero como usted señala claramente, la opción k espera que especifique los puntos de inicio y finalización de la clave, que pueden o no ser un solo campo. Su solución funciona perfectamente y, lo que es más importante, tengo claro por qué lo hace. Muchas gracias.
Harry

Esto es ENORME Muchas otras fuentes sobre KEYDEF hablan sobre -k1 -k2 sin enfatizar la importancia de COMMA en el formato para limitar qué columnas se consideran en cada paso de clasificación. Estuve atrapado en esto durante horas hasta que encontré esta respuesta. Y la página del manual es confusa aquí. No explica que las ubicaciones de "inicio y detención" se especifiquen con la notación de coma. ¡Gracias!
Jason Rohrer

16

Con GNU sortlo haces así, no estoy seguro acerca de MacOS:

sort -k2,2 -k1 <people.txt

Actualización según comentario. Citado de 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.

44
¿Podría por favor explicar esta extraña notación?
scai

1
Esto me hizo pensar en la línea correcta, gracias por eso. Pero no necesita especificar el punto de parada para el segundo -k. Eso es -k2,2 -k1,1 de lo contrario el punto de parada se toma como final de línea.
Harry

@ TonyBedford, correcto. Pero no especificar la posición de parada no cambiará el resultado para su entrada actual, pero forzará la coherencia en caso de que alguna vez tenga varias líneas con los campos 2 y 1. idénticos. Por lo tanto, prefiero permitir que la última -kincluya tanto como sea posible.
manatwork

1
@manatwork Eso no debería ser necesario; si todos los campos especificados se comparan igual, sortcomparará toda la línea. O con GNU sortpuede usar -spara una ordenación estable.
augurar
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.