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: $notfirst
e $last
são booleanos. A parte anterior ao último espaço ${f% *}
não é impressa para o primeiro campo, pois não existe tal coisa. $start
e $colon
conté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 gn
extensão, você poderá usar um g
modificador 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, g2
mas não sei).
Responder3
Uma perl
soluçã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 $count
variável e os relacionados if
sã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 intput
no 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