Tengo una línea (o muchas líneas) de números que están delimitados por un carácter arbitrario. ¿Qué herramientas de UNIX puedo usar para ordenar los elementos de cada línea numéricamente, conservando el delimitador?
Ejemplos incluyen:
- lista de números; entrada
10 50 23 42
:; ordenado:10 23 42 50
- Dirección IP; entrada
10.1.200.42
:; ordenado:1.10.42.200
- CSV; entrada
1,100,330,42
:; ordenado:1,42,100,330
- delimitado por tubos; entrada
400|500|404
:; ordenado:400|404|500
Dado que el delimitador es arbitrario, siéntase libre de proporcionar (o extender) una Respuesta utilizando un delimitador de un solo carácter de su elección.
sort
numeric-data
Jeff Schaller
fuente
fuente
cut
admite delimitadores arbitrarios con su-d
opción.4,325 comma 55 comma 42,430
no ocurriría, ni1.5 period 4.2
).Respuestas:
Puede lograr esto con:
reemplace los puntos
.
con su delimitador.agregue
-u
alsort
comando anterior para eliminar los duplicados.o con
gawk
( GNUawk
) podemos procesar muchas líneas, mientras que lo anterior también se puede ampliar:reemplace
*
como el separador de campoSEP='*'
con su delimitador .Notas:
Es posible que deba usar la
-g, --general-numeric-sort
opción de ensort
lugar de-n, --numeric-sort
manejar cualquier clase de números (entero, flotante, científico, hexadecimal, etc.).En
awk
ningún cambio de necesidad, seguirá manejando esos.fuente
Usando
perl
hay una versión obvia; dividir los datos, ordenarlos, volver a unirlos.El delimitador debe aparecer dos veces (una vez en
split
y una vez enjoin
)por ejemplo para un
,
Entonces
Como
split
es una expresión regular, el personaje puede necesitar una cita:Al usar las opciones
-a
y-F
, es posible eliminar la división. Con el-p
bucle, como antes y establezca los resultados en$_
, que se imprimirán automáticamente:fuente
-l
opción en lugar de usarchomp
. Eso también agrega la nueva línea al imprimir. Ver también-a
(con-F
) para la parte de división.-l
y-F
, es aún mejor:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
-l
opción; ¡Me lo había perdido!-F
indicador originalmente porque no funciona correctamente en todas las versiones (por ejemplo, su línea en CentOS 7 - perl 5.16.3 - devuelve un resultado en blanco, aunque funciona bien en Debian 9). Pero combinado con-p
él da un resultado un poco más pequeño, así que agregué eso como una alternativa a la respuesta. mostrando cómo-F
se puede usar. ¡Gracias!-a
y-n
opciones cuando-F
se usa y-n
cuando-a
se usa ... así que simplemente cambie-le
a-lane
Usando Python y una idea similar a la respuesta de Stephen Harris :
Entonces algo como:
Lamentablemente, tener que hacer la E / S manualmente hace que esto sea mucho menos elegante que la versión Perl.
fuente
Guión Bash:
Ejemplo:
Residencia en
Dividir la cadena en una matriz en Bash
Cómo ordenar una matriz en Bash
Unir elementos de una matriz?
fuente
Cáscara
Cargar un idioma de nivel superior lleva tiempo.
Para algunas líneas, el shell en sí mismo puede ser una solución.
Podemos usar el comando externo
sort
y el comandotr
. Uno es bastante eficiente en la clasificación de líneas y el otro es efectivo para convertir un delimitador en líneas nuevas:Esto necesita bash debido al uso de
<<<
solo. Si eso se reemplaza con un documento aquí, la solución es válida para posix.Este es capaz de ordenar campos con tabulaciones, espacios o caracteres shell glob (
*
,?
,[
). No nuevas líneas porque cada línea se está ordenando.Cambie
<<<"$2"
a<"$2"
para procesar nombres de archivos y llámelo como:El delimitador es el mismo para todo el archivo. Si eso es una limitación, podría mejorarse.
Sin embargo, un archivo con solo 6000 líneas tarda 15 segundos en procesarse. En verdad, el shell no es la mejor herramienta para procesar archivos.
Awk
Para más de unas pocas líneas (más de unos pocos 10) es mejor usar un lenguaje de programación real. Una solución awk podría ser:
Que toma solo 0.2 segundos para el mismo archivo de 6000 líneas mencionado anteriormente.
Comprenda que los
<"$2"
archivos for podrían cambiarse nuevamente<<<"$2"
por líneas dentro de variables de shell.Perl
La solución más rápida es perl.
Si desea ordenar un cambio de archivo
<<<"$a"
simplemente"$a"
y agregar-i
a las opciones de perl para que la edición del archivo esté "en su lugar":fuente
Utilizando
sed
para ordenar octetos de una dirección IPsed
no tiene unasort
función incorporada, pero si sus datos están lo suficientemente limitados en el rango (como con las direcciones IP), puede generar un script sed que implemente manualmente una ordenación de burbuja simple . El mecanismo básico es buscar números adyacentes que están fuera de servicio. Si los números están fuera de servicio, cámbielos.El
sed
script en sí contiene dos comandos de búsqueda e intercambio para cada par de números desordenados: uno para los primeros dos pares de octetos (obligando a que esté presente un delimitador final para marcar el final del tercer octeto), y un segundo para el tercer par de octetos (finaliza con EOL). Si se producen intercambios, el programa se bifurca a la parte superior del script, buscando números que están fuera de orden. De lo contrario, sale.El script generado es, en parte:
Este enfoque codifica el período como el delimitador, que debe ser escapado, ya que de lo contrario sería "especial" para la sintaxis de expresión regular (permitiendo cualquier carácter).
Para generar una secuencia de comandos sed, este bucle hará:
Redireccionar la salida de ese script a otro archivo, digamos
sort-ips.sed
.Una ejecución de muestra podría verse así:
La siguiente variación en el script generador utiliza los marcadores de límite de palabras
\<
y\>
elimina la necesidad de la segunda sustitución. Esto también reduce el tamaño del script generado de 1.3 MB a poco menos de 900 KB junto con una reducción considerable del tiempo de ejecución delsed
mismo (a aproximadamente 50% -75% del original, dependiendo de quésed
implementación se esté usando):fuente
sed
es ridículo, por eso es un desafío interesante.Aquí un golpe que adivina el delimitador por sí mismo:
Puede que no sea muy eficiente ni limpio, pero funciona.
Utilizar como
bash my_script.sh "00/00/18/29838/2"
.Devuelve un error cuando el mismo delimitador no se usa de manera consistente o cuando dos o más delimitadores se suceden.
Si el delimitador utilizado es un carácter especial, se escapa (de lo contrario,
sed
devuelve un error).fuente
Esta respuesta se basa en un malentendido de la Q., pero en algunos casos resulta ser correcta de todos modos. Si la entrada es números enteramente naturales y tiene un solo delimitador por línea (como con los datos de muestra en la Q), funciona correctamente. También manejará archivos con líneas que tienen cada uno su propio delimitador, que es un poco más de lo que se solicitó.
Esta función de shell
read
s de entrada estándar, utiliza la sustitución de parámetros POSIX para encontrar el delimitador específico en cada línea, (almacenado en$d
), y utilizatr
para reemplazar$d
con una nueva línea\n
ysort
los datos de esa línea, luego restaura los delimitadores originales de cada línea:Aplicado a los datos dados en el OP :
Salida:
fuente
Para delimitadores arbitrarios:
En una entrada como:
Da:
fuente
Esto debería manejar cualquier delimitador sin dígitos (0-9). Ejemplo:
Salida:
fuente
Con
perl
:Con
ruby
, que es algo similar aperl
Comando personalizado y pasar solo la cadena delimitador (no regex). Funcionará si la entrada también tiene datos flotantes
Comando personalizado para
perl
Lecturas adicionales: ya tenía esta lista útil de frases de perl / ruby
fuente
La siguiente es una variación de la respuesta de Jeff en el sentido de que genera un
sed
script que hará el tipo Bubble, pero es lo suficientemente diferente como para garantizar su propia respuesta.La diferencia es que en lugar de generar expresiones regulares básicas O (n ^ 2), esto genera expresiones regulares extendidas O (n). El script resultante tendrá aproximadamente 15 KB de tamaño. El tiempo de ejecución del
sed
script es en fracciones de segundo (se tarda un poco más en generar el script).Está restringido a la clasificación de enteros positivos delimitados por puntos, pero no se limita al tamaño de los enteros (solo aumenta
255
en el bucle principal) o al número de enteros. El delimitador se puede cambiar cambiandodelim='.'
el código.Se me ha ocurrido entender bien las expresiones regulares, así que me iré describiendo los detalles para otro día.
El guión se verá así:
La idea detrás de las expresiones regulares generadas es la coincidencia de patrones para números que son menores que cada número entero; esos dos números estarían fuera de servicio, por lo que se intercambian. Las expresiones regulares se agrupan en varias opciones OR. Preste mucha atención a los rangos agregados a cada elemento, a veces lo son
{0}
, lo que significa que el elemento inmediatamente anterior debe omitirse de la búsqueda. Las opciones de expresiones regulares, de izquierda a derecha, coinciden con números que son más pequeños que el número dado por:Para deletrear un ejemplo, tome
101
(con espacios adicionales para facilitar la lectura):Aquí, la primera alternancia permite los números del 100 al 100; la segunda alternancia permite 0 a 99.
Otro ejemplo es
154
:Aquí la primera opción permite 150 a 153; el segundo permite de 100 a 149, y el último permite de 0 a 99.
Prueba cuatro veces en un bucle:
Salida:
fuente
División de entrada en varias líneas
Utilizando
tr
, puede dividir la entrada utilizando un delimitador arbitrario en varias líneas.Esta entrada puede ejecutarse
sort
(usando-n
si la entrada es numérica).Si desea retener el delimitador en la salida, puede volver a usarlo
tr
para volver a agregar el delimitador.por ejemplo, usar el espacio como delimitador
cat input.txt | tr " " "\n" | sort -n | tr "\n" " "
entrada:
1 2 4 1 4 32 18 3
salida:1 1 2 3 4 4 18 32
fuente