Tengo un problema con la salida de un programa. Necesito ejecutar un comando en bash y tomar su salida (una cadena) y dividirla para agregar nuevas líneas en ciertos lugares. La cadena se ve así:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
básicamente es unxxx.yy.zz:valor, pero el valor puede contener espacios. Aquí está el resultado que me gustaría obtener
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Tengo la idea de buscar el primer punto y luego mirar hacia atrás desde esa posición en busca de espacio para colocar una nueva línea allí, pero no estoy seguro de cómo lograrlo en Bash.
Respuesta1
Solución bash pura, no se utilizan herramientas externas para procesar las cadenas, solo expansión 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
Explicación: $notfirst
y $last
son booleanos. La parte anterior al último espacio ${f% *}
no se imprime para el primer campo, ya que no existe tal cosa. $start
y $colon
contiene varias cadenas que separan los campos: en el primer elemento, notfirst + last
es 0, por lo que no se antepone nada, para el resto de las líneas, $notfirst
es 1, por lo que se imprime una nueva línea, y para la última línea, la suma da 2, por lo que se imprime un espacio. Luego, se imprime la parte posterior al último espacio ${f##* }
. Se imprimen dos puntos para todas las líneas excepto la última.
Respuesta2
Con GNU sed, puede hacer coincidir cada cadena contigua (es decir, sin espacios en blanco) terminada en :
y luego colocar una nueva línea antes de todas menos la primera:
sed 's/[^[:space:]]\+:/\n&/g2'
Si su versión de sed no admite la gn
extensión, puede usar un g
modificador simple
sed 's/[^[:space:]]\{1,\}:/\
&/g'
que funcionará igual excepto por imprimir una nueva línea adicional antes de la primera clave. Podrías usarlo perl -pe 's/\S+:/\n$&/g'
con la misma condición (puede haber un equivalente en Perl de GNU sed g2
pero no lo sé).
Respuesta3
Una perl
solución:
$ 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
Explicación
\S+:
coincide con el final de la cadena con:
.- Con todas las cadenas coincidentes, insertamos la nueva línea antes de ellas,
("\n$&")
excepto la primera($seen++)
.
Respuesta4
Aquí hay un enfoque ingenuo que debería funcionar suponiendo que no le importe que las tabulaciones y las nuevas líneas en la entrada (si las hay) se conviertan en espacios sin formato.
La idea es simple: dividir la entrada en espacios en blanco e imprimir cada token, excepto que anteponga los tokens que terminan con :
una nueva línea (y vuelva a agregar un espacio delante de los demás). La $count
variable y la relacionada if
solo son útiles para evitar una línea inicial vacía. Podría eliminarse si eso no es un problema. (El script supone que la entrada está en un archivo llamado intput
en el directorio actual).
#! /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 a alguien se le ocurra una alternativa mejor.
$ 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