Qual é a maneira mais fácil de classificar uma lista como esta

1

Estou tentando determinar qual versão do DB2 é a mais recente em um determinado nó. Instalamos nosso software DB2 no diretório / opt / IBM / db2. Se eu listar esse diretório, obtenho

V10.5
V9.1
V9.5
V9.5fp10
V9.7
V9.7fp3
V9.7fp6
V9.7fp7

Obviamente 10.5 é atualmente a versão mais recente, mas a primeira entrada nem sempre será a mais recente (ou seja: quando instalarmos o V11.0). Existe uma maneira relativamente fácil de determinar o mais recente (em ksh93)? Eu poderia analisar cada entrada em major / minor / fixpack, mas isso parece ser trabalhoso.

    
por Scavenger 06.12.2017 / 21:49

3 respostas

3

Se você tem ferramentas GNU, você pode usar ls -v

Ou use perl:

printf "%s\n" * | perl -e '
    @sorted = map {$_->[1]}
              sort {$a->[0] <=> $b->[0] or $a->[1] cmp $b->[1]}
              map {/(\d+\.\d*)/ and [$1, $_]}
              <>;
    print $sorted[-1];
'
    
por 06.12.2017 / 22:24
3

Sem ferramentas GNU e apenas o shell script de trabalho intensivo:

recentdb2 () (
  cd /opt/IBM/db2 || return 1
  highest=
  himajor=
  himinor=
  hifp=

  for dir in V*.*
  do
        [ ! -d "$dir" ] && continue
        nov="${dir#V*}"
        major="${nov%%.*}"
        notmajor="${nov##*.}"
        minor="${notmajor%%fp*}"
        if [ "$minor" = "$notmajor" ]
        then
                # no FP
                fp=0
        else
                fp="${notmajor##*fp}"
        fi

        # if highest major isn't set, set it and continue
        # else compare major; if higher, set it and continue
        # else compare minor; if higher, set it and continue
        # else compare fp; if higher, set it

        if [ "${himajor:-notset}" = "notset" ]
        then
                highest="$dir"
                himajor="$major"
                himinor="$minor"
                hifp="$fp"
                continue
        fi

        if [ "$major" -gt "$himajor" ]
        then
                highest="$dir"
                himajor="$major"
                himinor="$minor"
                hifp="$fp"
                continue
        elif [ "$major" -eq "$himajor" ] && [ "$minor" -gt "$himinor" ]
        then
                highest="$dir"
                himajor="$major"
                himinor="$minor"
                hifp="$fp"
                continue
        elif [ "$major" -eq "$himajor" ] && [ "$minor" -eq "$himinor" ] && [ "$fp" -gt "$hifp" ]
        then
                highest="$dir"
                himajor="$major"
                himinor="$minor"
                hifp="$fp"
        fi
        # else, the current value is less than the highest value, drop it and continue on
  done
  printf "%s" "$highest"
)

Isso define uma função que (tentará) retornar o diretório do DB2 de nível mais alto a partir de / opt / IBM / db2.

A função é toda executada em um subshell, de modo que:

  • as variáveis criadas desaparecem quando são concluídas e
  • o cd também é isolado para o subshell

A função então faz um loop sobre as entradas no diretório / opt / IBM / db2 que correspondem ao padrão de glob V*.* - ajuste isso se você puder ter versões como V11 sem nenhum ponto. O primeiro teste é garantir que não somos enganados por um arquivo perdido que corresponda a esse padrão.

O trabalho começa: nós tiramos o primeiroV off ("no V"), então computamos:

  • o maior número é tudo antes do primeiro período
  • o "não maior número" é tudo depois do primeiro período
  • o número menor é a parte do "não maior número" até que fp
  • se houver um fp no nome (não houve alteração da cadeia completa "não maior número" para o número menor), defina o fp para isso, caso contrário, defina-o como zero

Como os comentários dizem, testamos e definimos as variáveis "altas" apropriadamente. O primeiro diretório que encontrarmos entrará na primeira condição - onde é o nível mais alto por padrão.

Entradas de diretório subseqüentes são então comparadas com as versões maior, menor e fp atualmente mais altas.

Quando o loop estiver completo, a função imprime o nome do diretório mais recente.

Use a função criando um script ou fonte no código e, em seguida, chame a função:

h=$(recentdb2)
    
por 07.12.2017 / 16:29
2

com zsh :

list=(/opt/IBM/db2/V*(Nn:t))

em que o qualificador n glob ativa a classificação numérica que faz exatamente o que você deseja, classifique da mais antiga ( $list[1] ) para a mais recente ( $list[-1] ).

Com ksh93 , se tiver sido criado com o ls incorporado em ast-open , você pode usar a classificação versão :

(cd /opt/IBM/db2 && command /opt/ast/bin/ls -d -y version V*)

command /opt/ast/bin/ls chama o comando /opt/ast/bin/ls ou o equivalente embutido correspondente, se presente, independentemente de existir /opt/ast/bin/ls ou não no sistema de arquivos.

Você também pode fazer:

PATH=/opt/ast/bin:$PATH
ls -d -y version V*

Para fazer com que ls embutido tenha precedência sobre o sistema em /bin ou /usr/bin .

Caso contrário, você poderia fazer:

tmp=(/opt/IBM/db2/~(N)V*) # ~(N) equivalent of zsh's N
tmp=("${tmp[@]##*/}")     # equivalent of :t
typeset -A map

# build an associative array where the key is the element with
# all the numbers in them 0-padded to 20 digits. Append a counter
# to make sure all keys are unique.
typeset -Z 20 n=0
for i in "${tmp[@]}"; do
  k=${i//+([0-9])/00000000000000000000}
  k=${k//+(0){20}([0-9])/}
  map[$k-$n]=$i
  ((n++))
done

# sort the keys of the associative array into $@:
set -s -- "${!map[@]}"

# fill $list with the values for those (now sorted) keys.
typeset -a list
for i do
  list+=("${map[$i]}")
done
printf -- '- %s\n' "${list[@]}"
((${#list[@]})) && printf 'Newest: %s\n' "${list[@]: -1}"

Na sua amostra, isso dá:

- V9.1
- V9.5
- V9.5fp10
- V9.7
- V9.7fp3
- V9.7fp6
- V9.7fp7
- V10.5
Newest: V10.5
    
por 07.12.2017 / 16:39