bash - Separa valores de “tabela” em strings no array

2

EDIT: desculpe, a saída que eu aleguei está errada. Existem mais espaços do que eu pensava anteriormente (algo aconteceu quando a saída foi salva no arquivo html para removê-los) A saída real é a seguinte:

user@Debian:~$ sudo smartctl -l selftest /dev/sda | grep -e "#"
# 1  Short offline       Completed without error       00%      7264         -
# 2  Short offline       Completed without error       00%      7240         -
# 3  Short offline       Completed without error       00%      7219         -
# 4  Short offline       Completed without error       00%      7192         -
# 5  Short offline       Completed without error       00%      7168         -
# 6  Short offline       Completed without error       00%      7144         -
# 7  Extended offline    Completed without error       00%      7125         -
# 8  Short offline       Completed without error       00%      7096         -
# 9  Short offline       Completed without error       00%      7072         -
#10  Short offline       Completed without error       00%      7049         -
#11  Short offline       Completed without error       00%      7004         -

Não tenho certeza se estou usando a terminologia correta, pois sou bastante novo no Linux / bash.

De qualquer forma, estou usando o Smartmontools para detectar e notificar-me se houver algum erro do SMART. Ele está funcionando como eu quero, mas eu gostaria de obter algumas estatísticas diárias sobre o HDD, então eu fiz meu próprio script que coleta informações de smartmontools e outras coisas interessantes (como temporários, valores SMART e espaço HDD usado). Pode não ser a melhor maneira de fazer algo assim, mas eu gosto de fazer isso e estou aprendendo enquanto vou.

O e-mail que estou enviando é formatado como HTML para criar tabelas e adicionar cores de fonte para resultados positivos / negativos (verde / vermelho). Mas quando tentei criar uma tabela para exibir autotestes, tive alguns problemas.

O comando que estou usando é: sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE (em um loop onde $ HDD é todo o disco rígido no meu sistema e $ SMARTFILE é o arquivo html que estou salvando.

A saída deste comando é assim:

# 1 Short offline Completed without error 00% 7264 -

# 2 Short offline Completed without error 00% 7240 -

E assim por diante. Eu estou usando o seguinte código para obter o número de série da unidade:

HDDinfo="$(sudo smartctl --info $HDD | grep -e 'Serial Number')"
IFS=':' read -r -a array <<< "$HDDinfo"

Como sudo smartctl --info $HDD | grep -e 'Serial Number' normalmente produz

Serial Number: WD-RESTOFS/N123

Mas para colocá-lo em uma tabela, separei a string usando o caractere ':' e obtive uma matriz como esta:

Serial Number,WD-RESTOFS/N123

Mas com a saída que obtenho para sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE , não há (para mim) uma maneira óbvia de separá-los e a maneira como fiz isso antes não funcionará, pois as sequências que quero têm espaços neles e, portanto, não ser separado usando um caractere de espaço.

TL; DR, tenho o seguinte comando sudo smartctl -l selftest /dev/sda | grep '#' >> $SMARTFILE que tem uma saída como esta:

# 1 Short offline Completed without error 00% 7264 -

# 2 Short offline Completed without error 00% 7240 -

Eu quero fazer uma matriz (ou similar) para armazená-los individualmente assim:

# 1,Short offline,Completed without error,00%,7264,-

Para que eu possa colocá-lo facilmente em uma tabela HTML. Isso pode ser feito? Se ocorrer um erro, poderá parecer algo assim:

# 1 Short offline Completed: read failure 20% 717 555027747

Por favor, deixe-me saber se algo não está claro ou se há qualquer outra informação necessária.

    
por L.Johnson 07.01.2017 / 00:10

2 respostas

3

A partir da sua (pequena) amostra de smartctl mensagens acima, parece que suas partes são basicamente separadas por "< espaço > < tudo menos uma minúscula >" (exceto pelo campo "# nnn" no início da linha).

sed poderia ajudar a separar as partes:

$ smartctl_output="\                                           
# 1 Short offline Completed without error 00% 7264 -
# 2 Short offline Completed without error 00% 7240 -
# 1 Short offline Completed: read failure 20% 717 555027747"

$ csv="$( sed 's/ //; s/ \([^[:lower:]]\)/,/g' <<< "$smartctl_output" )"

$ echo "$csv"
#1,Short offline,Completed without error,00%,7264,-
#2,Short offline,Completed without error,00%,7240,-
#1,Short offline,Completed: read failure,20%,717,555027747

Se é isso que você quer, agora você pode preencher sua matriz como fez com o HDDinfo.

[atualização]

Aqui está uma explicação sobre a parte sed que faz a divisão: o programa sed é feito de duas partes que eu coloquei em uma linha. Aqui está a versão expandida:

sed '
    s/ //
    s/ \([^[:lower:]]\)/,/g
'

Um programa sed opera em cada linha de entrada: lê uma linha, aplica um conjunto de transformações e imprime a linha. Então começa de novo com a próxima linha até que não haja mais linhas para ler.

Aqui, o primeiro sed command s/ // exclui o primeiro espaço para unir "#" e o número seguinte.

Em seguida, o segundo sed command s/ \([^[:lower:]]\)/,/g pesquisa o início de cada campo (conforme definido por "< espaço > < tudo, menos um > minúsculo") e substitui o espaço por dois pontos. O refere-se à expressão regular entre parênteses " \([^[:lower:]]\) ", que representa o primeiro caractere do próximo campo.

A parte restante é um teste: em vez de alimentar sed com o conteúdo de um arquivo ou a saída de um comando, eu o alimentei com a variável smartctl_output (uma string feita de suas amostras) e eu atribuí o resultará na variável csv .

[atualização # 2]

Aparece agora que os campos estão separados por dois ou mais espaços. É ainda mais fácil do que anteriormente. O comando sed torna-se:

sed 's/  \+/,/g'

O que significa: substituir todas as séries de dois ou mais espaços por dois pontos.

    
por 07.01.2017 / 00:44
0

Não consigo pensar em uma maneira de fazer isso nativamente no shell, mas em perl , por exemplo, você poderia definir uma expressão regular para a divisão de campo e usá-la para inserir uma única delimitador de sua escolha, que poderia então ser lido simplesmente usando IFS=, ou qualquer outra coisa.

Com base na sua amostra, os campos podem ser divididos em um espaço seguido por:

  1. um caractere maiúsculo ou hífen; ou
  2. uma sequência de pelo menos dois dígitos

Então, direcione seu comando para algo como

. . . | 
  perl -F'[[:space:]](?=[[:upper:]-]|[[:digit:]]{2,})' -anle 'print join ",", @F'
    
por 07.01.2017 / 01:09