Como posso classificar uma lista com o nível major.minor.patch e, às vezes, com o rc corretamente?

15

Eu tenho que ordenar a seguinte lista com um script de shell e fazer a versão mais recente aparecer na parte inferior ou superior. Como eu faria isso apenas com ferramentas 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
    
por Mandragor 09.03.2017 / 17:23

5 respostas

15

A classificação GNU tem -V , que pode lidar principalmente com uma lista como essa ( detalhes ):

 -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

No entanto, essas .rc* versões podem ser um pouco problemáticas, uma vez que elas provavelmente devem ser classificadas antes da versão não-rc correspondente, se houver ambas, ou seja. Alguns sistemas de versionamento (como o do Debian) usam sufixos começando com um til ( ~ ) para marcar os pré-lançamentos, e eles ordenam antes da versão sem um sufixo, que ordena antes das versões com outros sufixos. Aparentemente isso é suportado por pelo menos o sort no meu sistema, como mostrado acima ( sort (GNU coreutils) 8.23 ).

    
por 09.03.2017 / 17:29
6

Confira sort -V :

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

Os números de versão são bestas complicadas, com muito poucos padrões governando as porções alfabéticas, mas tente isso em seus dados reais e veja se é suficiente.

    
por 09.03.2017 / 17:29
3

Isso pode ser feito como uma linha, mas dividido em várias linhas (nos canais) para facilitar a leitura, e lida com rc também.

Se você não tiver uma opção -V para o seu tipo, ou mesmo se fizer isso, precisará lidar ocasionalmente com 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/([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)/...rc/" -e "s/^/release-/"

O primeiro sed retira os caracteres não numéricos
O sort usa um . delimitador ( -t. ), classificação numérica (- n ) e chaves ( -k )
O% final sed coloca os caracteres não numéricos de volta no lugar.

    
por 09.03.2017 / 18:18
0

Obrigado por toda a inspiração - Posso propor minha própria resposta: O programa de classificação pode ser induzido a fazer o que é necessário. No final, trata-se de adicionar um quarto número ao versionamento de 3 dígitos, classificá-lo e depois removê-lo novamente. Obras - solução mais simples até agora, IMHO.

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

resultado:

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

....
    
por 11.03.2017 / 12:09
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

$
    
por 11.03.2017 / 22:34