Bash: Group strings contendo a mesma substring

0

Vamos imaginar no meio de um script bash uma matriz de strings como esta:

example1.log    
example1.log.1
log2.log
xpto.log
xpto.log.1
xpto.log.2

Existe uma maneira de eu fazer isso em um array associativo contendo as strings em um formato como este (representação da estrutura de array desejada):

{
    "example1" : [ "example1.log", "example1.log.1" ],
    "log2" : [ "log2.log" ],
    "xpto" : [ "xpto.log", "xpto.log.1", "xpto.log.2" ]
}

Notas:

  • As strings iniciais sempre terão um formato como non-predictable-name + .log + .x (ponto opcional e número int)

Obrigado.

    
por TCB13 29.03.2017 / 01:19

3 respostas

1

Aqui está uma solução com bash (versão > = 4):

declare -A logs
for logfile in *; do
    logs[${logfile%%.*}]+="$logfile "
done

Como exemplo de uso do seu array associativo, você pode imprimi-lo assim:

for log in "${!logs[@]}"; do
    echo "$log:"
    for file in ${logs[$log]}; do
        echo "- $file"
    done
done

(suponho que nenhum dos seus arquivos de log tenha um espaço em seu nome)

    
por 29.03.2017 / 03:11
1

Embora as matrizes associativas bash tenham sido inspiradas a partir de ksh93 (com todas as suas verrugas), ela não suporta a atribuição de nada além de escalares para seus valores.

Para um array associativo cujos valores podem ser arrays, você precisa de ksh93 :

$ ksh -c 'typeset -A logs; for f in *.log*; do logs[${f%.log*}]+=($f); done; typeset -p logs'
typeset -A logs=([example1]=(example1.log example1.log.1) [log2]=(log2.log) [xpto]=(xpto.log xpto.log.1 xpto.log.2) )

Para passar por cima deles:

typeset -A logs
for f in *.log*; do logs[${f%.log*}]+=($f); done

for key in "${!logs[@]}"; do
  printf '%s:\n' "$key"
  for file in "${logs[$key][@]}"; do
     printf ' File: "%s"\n' "$file"
  done
done
Matrizes associativas de

zsh também podem ter valores escalares, mas como esses valores podem conter o caractere NUL, eles podem armazenar listas de nomes de arquivos arbitrários (delimitando-os com caracteres NUL). Com bash , aqui, como os nomes dos arquivos estão todos no diretório atual, você pode delimitar com / . Ou se você puder supor que um caractere específico não ocorre nos nomes dos arquivos (como a abordagem do @xhienne com o caractere de espaço), você pode usá-lo. Observe também que as matrizes associativas bash têm essa limitação de que a chave não pode estar vazia.

    
por 29.03.2017 / 14:59
0
cat - <<\eof |
example1.log
example1.log.1
log2.log
xpto.log
xpto.log.1
xpto.log.2
eof
perl -lne '

/(.*)\.log(?=\.\d+$|$)/ and
   push(@{$h{$1}}, qq/"$_"/), $h[-1+keys %h]=$1;

END{

   print for 
      q/{/,

      join(",\n",
         map {
            join($",
               qq/\t"$_"/,
               q/:/,
               q/[/,
               join(", ", @{$h{$_}}),
               q/]/,
            )
         } @h
      ),

      q/}/

}
'

Saída

{
   "example1" : [ "example1.log", "example1.log.1" ],
   "log2" : [ "log2.log" ],
   "xpto" : [ "xpto.log", "xpto.log.1", "xpto.log.2" ]
}
    
por 29.03.2017 / 14:35