Arrays multidimensionais Programação Shell e Scripting

1

Eu tenho dois arquivos:

file1: É uma lista do número de interfaces no switch:

1/1
1/2
1/3
1/4
1/5
.
.

arquivo-2: contém VLAN-ID, VLAN-NAME, Interfaces pertencente a essa VLAN

1,"vlan-wifi",1/1,1/7,1/8
2,"vlan-admin",1/3,1/5,1/6
3," " ,3/3,1/3

Estou tentando usar esses arquivos para criar uma tabela como:

                    |   interface Switch
VLAN-ID | VLAN-NAME |1/1|1/2|1/3|1/4|1/5|1/6|1/7|1/8|1/9|1/10|...|3/3
    1   |  vlan-wifi| * |   |   |   |   |   | * | * |   |    |   |
    2   | vlan-admin|   |   | * |   | * | * |   |   |   |    |   |
    3   |           |   |   | * |   |   |   |   |   |   |    |   | *

Como posso conseguir isso?

    
por Jcob 09.08.2017 / 16:22

4 respostas

2

Você não quer usar loops de shell para processar texto . Aqui, é um trabalho típico para awk :

awk -F '"? *, *"?' '
   !ports_processed {
      port[++ports] = $0; width[ports] = length; next
   }
   FNR==1 {
     printf "                    |   interface Switch\nVLAN-ID | VLAN-NAME "
     for (i = 1; i <= ports; i++) printf "|%s", port[i]
     print ""
   }
   {
     printf "%6d  |%11s", $1, $2
     split("", member)
     for (i = 3; i <= NF; i++) member[$i]
     for (i = 1; i <= ports; i++)
       printf "| %*s", 1 - width[i], (port[i] in member ? "*" : " ")
     print ""
   }' file-1 ports_processed=1 file-2

(aqui assumindo que os nomes de vlan não contenham vírgulas ou aspas duplas ou novas linhas (precisamos saber como eles são codificados se isso ocorrer))

Para responder a questão no assunto, para um shell com suporte à matriz multidimensional, consulte ksh93 . Também possui recursos de análise e gravação de csv.

    
por 11.08.2017 / 11:50
1

Aqui está uma maneira de fazer isso usando Python. Observe que arquivo1 da sua pergunta não parece ser necessário, portanto, esse script usa apenas um parâmetro:

Código:

import sys

filename = sys.argv[1]
vids = {}
intfs = set()
with open(filename, 'rU') as f:
    for line in f:
        vid, vname, vintf = line.strip().split(',', 2)
        vintf = vintf.split(',')
        vids[int(vid)] = (vname.strip('"').strip(), vintf)
        intfs |= set(vintf)

intfs = [(i, len(i)) for i in
         sorted(intfs, key=lambda x: tuple(int(y) for y in x.split('/')))]

max_name_len = max(len(i[0]) for i in vids.values()) + 1

line = ('VLAN-ID |%%%ds' % max_name_len) % (
    'VLAN-NAME' + ' ' * ((max_name_len - 9) / 2))
for intf, width in intfs:
    line += ('|%%%ds' % width) % intf
print(line)

fmt = '%%5s   |%%%ds' % max_name_len
for vid, values in sorted(vids.items()):
    vname, have_intfs = values
    line = fmt % (vid, vname)
    for intf, width in intfs:
        line += ('| %%-%ds' % (width-1)) % (
            '*' if intf in have_intfs else '')
    print(line)

Resultados:

VLAN-ID | VLAN-NAME |1/1|1/3|1/5|1/6|1/7|1/8|1/10|3/3
    1   |  vlan-wifi| * |   |   |   | * | * |    |   
    2   | vlan-admin|   | * | * | * |   |   |    |   
    3   |           |   | * |   |   |   |   | *  | * 
    
por 10.08.2017 / 06:06
1

Você pode fazer isso usando bash e matrizes conforme mostrado:

#!/bin/bash

# store the numerically sorted file1 into array A
A=( $(sort -t/ -k1,1n -k2,2n < file-1) )

# determine the maximum field width by examining the lengths of the 2nd field
# and the string "VLAN-NAME" coming for the header.
maxW=$(
         { echo ',"VLAN-NAME"'; cat < file-2; } |
         cut -d, -f2 | awk '{print length-2}' |
         sort -nr | sed q
      )

# first two fields of the header
B=( "VLAN-ID " "$(printf " %-${maxW}s\n" "VLAN-NAME")" )

# complete header: from array B and the numerically sorted file1 contents
C=( "${B[@]}" "$(IFS="|"; echo "${A[*]}")" )

# display the header
echo "$(IFS="|"; echo "${B[*]}")" |\
sed -e 's/[^[:blank:]]/ /g;s/$/|   interface switch/'
echo "$(IFS="|"; echo "${C[*]}")"

# remaining lines printed
while IFS=, read -ra B; do
   D=(x)
   for arg in "${A[@]}"; do
      case " ${B[*]:2} " in *" $arg "* ) res=* ;; * ) res=\ ;; esac
      var1=${arg//[!\/]/ } D=( "${D[@]}" "${var1/\//$res}" )
   done
   printf "%8s| %${maxW}s|%s\n" "${B[0]}" "${B[1]//\"/}" "$(IFS="|";echo "${D[*]:1}")"
done < file-2

Resultados

                    |   interface switch
VLAN-ID | VLAN-NAME |1/1|1/2|1/3|1/4|1/5|1/6|1/7|1/8|1/9|1/10|3/3
       1|  vlan-wifi| * |   |   |   |   |   | * | * |   |    |
       2| vlan-admin|   |   | * |   | * | * |   |   |   |    |
       3|           |   |   | * |   |   |   |   |   |   |    | *
    
por 11.08.2017 / 09:53
1

Você pode usar os utilitários tbl , nroff se não quiser ficar incomodado com as larguras de campo:

perl -F'\"?,\"?' -lane '
   BEGIN {
      chomp(@A = qx(sort -t/ -k1,1n -k2,2n < $ARGV[0]));
      shift;

      # prepare table for the tbl utiity
      print q/.TS/;
      print join(",", qw/allbox center/, qq/tab(\t);/)       ;
      print join($",  qw/c s c/,         qw/s/ x $#A)        ;
      print join($",  qw/c r/,           qw/c/ x  @A), qw/./ ;
      print join("\t", $,, q/interface switch/);
      print join("\t", qw/VLAN-ID VLAN-NAME/, @A);
   }

    print
join("\t", @F[0,1], map { my $e = $_; (0 < grep { $e eq $_ } @F[2..$#F]) ? q[*] : q[] } @A);

   END { print q/.TE/; }
' file-1 file-2 | tbl - | nroff -Tascii -ms | sed -n '/./!d;$!N;s/.*\n.\(.*\).//p'

Resultados:

                     |                         interface switch                         
VLAN-ID |  VLAN-NAME | 1/1 | 1/2 | 1/3 | 1/4 | 1/5 | 1/6 | 1/7 | 1/8 | 1/9 | 1/10 | 3/3 
   1    |  vlan-wifi |  *  |     |     |     |     |     |  *  |  *  |     |      |     
   2    | vlan-admin |     |     |  *  |     |  *  |  *  |     |     |     |      |     
   3    |            |     |     |  *  |     |     |     |     |     |     |      |  *  
    
por 11.08.2017 / 13:55