¿Cómo puedo ordenar una lista con el nivel major.minor.patch y, a veces, rc correctamente?

18

Tengo que ordenar la siguiente lista con un script de shell y hacer que la última versión aparezca en la parte inferior o superior. ¿Cómo haría eso solo con herramientas de shell?

release-5.0.0.rc1
release-5.0.0.rc2
release-5.0.0
release-5.0.1
release-5.0.10
release-5.0.11
release-5.0.13
release-5.0.14
release-5.0.15
release-5.0.16
release-5.0.17
release-5.0.18
release-5.0.19
release-5.0.2
release-5.0.20
release-5.0.21
release-5.0.22
release-5.0.23
release-5.0.24
release-5.0.25
release-5.0.26
release-5.0.27
release-5.0.28
release-5.0.29
release-5.0.3
Mandragor
fuente
1
Ver también printf '%s\n' ${(on)array}en zsh. (cuando la lista está en la $arraymatriz).
Stéphane Chazelas

Respuestas:

20

GNU sort tiene -Vque en su mayoría puede lidiar con una lista como esa ( detalles ):

 -V, --version-sort
        natural sort of (version) numbers within text

$ cat vers
release-5.0.19
release-5.0.19~pre1
release-5.0.19-bigbugfix
release-5.0.2
release-5.0.20
$ sort -V vers
release-5.0.2
release-5.0.19~pre1
release-5.0.19
release-5.0.19-bigbugfix
release-5.0.20

Sin embargo, esas .rc*versiones podrían ser un poco problemático, ya que probablemente deberían clasificarse antes de la versión no rc correspondiente, si es que hay ambas, es decir. Algunos sistemas de versiones (como Debian), usan sufijos que comienzan con tilde ( ~) para marcar versiones preliminares, y se ordenan antes de la versión sin sufijo, que se clasifica antes que las versiones con otros sufijos. Aparentemente, esto es compatible al menos sorten mi sistema, como se muestra arriba ( sort (GNU coreutils) 8.23).

ilkkachu
fuente
3
Me golpeó por un par de segundos :)
rosuav
1
Muchas gracias. Ni siquiera imaginé que ese tipo tendría una opción como esa.
Mandragor
FWIW, -Vtambién soportado por defecto sorten OpenBSD, pero no en NetBSD.
Kusalananda
FreeBSD también, aparentemente , la página del manual allí también tiene un orden de salida de ejemplo, y se ve idéntico al de OpenBSD.
ilkkachu
7

Echa un vistazo a sort -V:

   -V, --version-sort
          natural sort of (version) numbers within text

Los números de versión son bestias complicadas, con muy pocos estándares que rigen las porciones alfabéticas, pero intente esto en sus datos reales y vea si es suficiente.

rosuav
fuente
¡Wow asombroso! Esto funciona incluso para un viejo problema mío con nombres de archivo mayorNumber–minorNumer some text, donde la clasificación de campos falla debido al delimitador Unicode. ¡Gracias por la pista!
Philippos
3

Esto se puede hacer como una línea, pero dividir en varias líneas (en las tuberías) aquí para facilitar la lectura, y también maneja las rc's.

Si no tiene una -Vopción para su tipo, o incluso si la tiene, tendrá que lidiar con los ocasionales rc:

cat versionlist |
sed -e "s/release-//" -e "s/rc//" |
sort -t. -n -k1,1 -k2,2 -k3,3 -k4,4 |
sed -r -e "s/([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)/\1.\2.\3.rc\4/" -e "s/^/release-/"

El primero sedelimina los caracteres no numéricos.
El sortutiliza un .delimitador ( -t.), ordenación numérica (- n) y teclas ( -k).
El último sedvuelve a colocar los caracteres no numéricos.

MikeD
fuente
0

Gracias por toda la inspiración. ¿Puedo proponer mi propia respuesta? Se puede engañar al programa de clasificación para que haga lo que se necesita. Al final, se trata de agregar un cuarto número a la versión de 3 dígitos, ordenarlo y luego eliminarlo nuevamente. Funciona: la solución más simple hasta ahora, en mi humilde opinión.

cat versionlist |\
sed -r "s/([0-9]+\.[0-9]+\.[0-9]+$)/\1\.99999/"|sort -V|sed s/\.99999$//

resultado:

release-5.0.0.rc1
release-5.0.0.rc2
release-5.0.0

....
Mandragor
fuente
0
$ cat /tmp/tmp.tmp
release-5.0.0.rc1
release-5.0.0.rc2
release-5.0.0
release-5.0.1
release-5.0.10
release-5.0.11
release-5.0.13
release-5.0.14
release-5.0.15
release-5.0.16
release-5.0.17
release-5.0.18
release-5.0.19
release-5.0.2
release-5.0.20
release-5.0.21
release-5.0.22
release-5.0.23
release-5.0.24
release-5.0.25
release-5.0.26
release-5.0.27
release-5.0.28
release-5.0.29
release-5.0.3

$ cat /tmp/tmp.tmp | awk -F\- '{ print $2,$1 }' | sort -n | awk '{ print $2 "-" $1 }'
release-5.0.0
release-5.0.0.rc1
release-5.0.0.rc2
release-5.0.1
release-5.0.10
release-5.0.11
release-5.0.13
release-5.0.14
release-5.0.15
release-5.0.16
release-5.0.17
release-5.0.18
release-5.0.19
release-5.0.2
release-5.0.20
release-5.0.21
release-5.0.22
release-5.0.23
release-5.0.24
release-5.0.25
release-5.0.26
release-5.0.27
release-5.0.28
release-5.0.29
release-5.0.3

$
jinete
fuente
La versión 5.0.0 debería aparecer DESPUÉS de .rc1 y .rc2 en la lista. Ese es el desafío aquí.
Mandragor