Como dividir uma string em um array no bash

Como dividir uma string em um array no bash

Eu tenho um problema com a saída de um programa. Preciso iniciar um comando no bash e pegar sua saída (uma string) e dividi-la para adicionar novas linhas em determinados lugares. A string fica assim:

battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500

basicamente é umxxx.yy.zz:valor, mas o valor pode conter espaços. Aqui está o resultado que eu gostaria de obter

battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500 

Tenho a ideia de procurar o primeiro ponto e depois olhar para trás a partir dessa posição em busca de espaço para colocar uma nova linha lá, mas não tenho certeza de como fazer isso no Bash.

Responder1

Solução bash pura, sem ferramentas externas usadas para processar as strings, apenas expansão de parâmetros:

#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'

IFS=: read -a fields <<< "$str"

for (( i=0 ; i < ${#fields[@]} ; i++ )) ; do
    f=${fields[i]}

    notfirst=$(( i>0 ))
    last=$(( i+1 == ${#fields[@]} ))

    (( notfirst )) && echo -n ${f% *}

    start=('' $'\n' ' ')
    colon=('' ': ')
    echo -n "${start[notfirst + last]}${f##* }${colon[!last]}"
done
echo

Explicação: $notfirste $lastsão booleanos. A parte anterior ao último espaço ${f% *}não é impressa para o primeiro campo, pois não existe tal coisa. $starte $coloncontém várias strings que separam os campos: no primeiro item, notfirst + lasté 0, então nada é acrescentado, para o resto das linhas, $notfirsté 1, então uma nova linha é impressa, e para a última linha, a adição dá 2, então um espaço é impresso. Então, a parte após o último espaço é impressa ${f##* }. Dois pontos são impressos para todas as linhas, exceto a última.

Responder2

Com o GNU sed, você pode combinar cada string contígua (ou seja, sem espaços em branco) terminada por :e então colocar uma nova linha antes de todas, exceto a primeira:

sed 's/[^[:space:]]\+:/\n&/g2'

Se a sua versão do sed não suportar a gnextensão, você poderá usar um gmodificador simples

sed 's/[^[:space:]]\{1,\}:/\
&/g'

que funcionará da mesma forma, exceto pela impressão de uma nova linha adicional antes da primeira chave. Você poderia usar perl -pe 's/\S+:/\n$&/g'com a mesma condição (pode haver um equivalente perl do GNU sed, g2mas não sei).

Responder3

Uma perlsolução:

$ perl -pe 's{\S+:}{$seen++ ? "\n$&" : "$&"}ge' file
battery.charge: 90 
battery.charge.low: 30 
battery.runtime: 3690 
battery.voltage: 230.0 
device.mfr: MGE UPS SYSTEMS 
device.model: Pulsar Evolution 500

Explicação

  • \S+:corresponde ao final da string com :.
  • Com todas as strings correspondentes, inserimos a nova linha antes delas, ("\n$&")exceto a primeira ($seen++).

Responder4

Aqui está uma abordagem ingênua que deve funcionar, assumindo que você não se importa que tabulações e novas linhas na entrada (se houver) sejam convertidas em espaços simples.

A ideia é simples: divida a entrada em espaços em branco e imprima todos os tokens, exceto que você acrescente os tokens que terminam com :uma nova linha (e adicione novamente um espaço na frente dos outros). A $countvariável e os relacionados ifsão úteis apenas para evitar uma linha inicial vazia. Pode ser removido se isso não for um problema. (O script assume que a entrada está em um arquivo chamado intputno diretório atual.)

#! /bin/bash

count=0
for i in $(<input) ; do
   fmt=
   if [[ $i =~ :$ ]] ; then
       if [[ $count -gt 0 ]] ; then
           fmt="\n%s"
       else
           fmt="%s"
       fi
       ((count++))
   else
       fmt=" %s"
   fi
   printf "$fmt" "$i"
done
echo
echo "Num items: $count"

Espero que alguém possa encontrar uma alternativa melhor.

$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6

informação relacionada