Cómo dividir una cadena en una matriz en bash

Cómo dividir una cadena en una matriz en bash

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: $notfirsty $lastson booleanos. La parte anterior al último espacio ${f% *}no se imprime para el primer campo, ya que no existe tal cosa. $starty $coloncontiene varias cadenas que separan los campos: en el primer elemento, notfirst + lastes 0, por lo que no se antepone nada, para el resto de las líneas, $notfirstes 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 gnextensión, puede usar un gmodificador 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 g2pero no lo sé).

Respuesta3

Una perlsolució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 $countvariable y la relacionada ifsolo 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 intputen 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

información relacionada