У меня проблема с выводом программы. Мне нужно запустить команду в bash и взять ее вывод (строку) и разделить ее, чтобы добавить новые строки в определенных местах. Строка выглядит так:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
в основном этоxxx.yy.zz:значение, но значение может содержать пробелы. Вот вывод, который я хотел бы получить
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
У меня есть идея найти первую точку, а затем вернуться назад от этой позиции в поисках места, чтобы поместить туда новую строку, но я не уверен, как это сделать в Bash.
решение1
Чистое решение bash, без использования внешних инструментов для обработки строк, только расширение параметров:
#! /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
Объяснение: $notfirst
и $last
являются логическими значениями. Часть перед последним пробелом ${f% *}
не печатается для первого поля, так как такого понятия нет. $start
и $colon
содержат различные строки, разделяющие поля: в первом элементе notfirst + last
— 0, поэтому ничего не добавляется, в остальных строках $notfirst
— 1, поэтому печатается новая строка, а в последней строке сложение дает 2, поэтому печатается пробел. Затем печатается часть после последнего пробела ${f##* }
. Двоеточие печатается для всех строк, кроме последней.
решение2
С помощью GNU sed можно сопоставить каждую непрерывную строку (т. е. без пробелов), завершающуюся символом , :
а затем поместить символ новой строки перед всеми строками, кроме первой:
sed 's/[^[:space:]]\+:/\n&/g2'
Если ваша версия sed не поддерживает gn
расширение, вы можете использовать простой g
модификатор
sed 's/[^[:space:]]\{1,\}:/\
&/g'
что будет работать так же, за исключением печати дополнительной новой строки перед первым ключом. Вы можете использовать perl -pe 's/\S+:/\n$&/g'
с тем же условием (возможно, есть эквивалент GNU sed на perl, g2
но я его не знаю).
решение3
Решение perl
:
$ 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
Объяснение
\S+:
соответствует строке, заканчивающейся на:
.- Для всех совпавших строк мы вставляем новую строку перед ними,
("\n$&")
за исключением первой($seen++)
.
решение4
Вот наивный подход, который должен работать, если вас не волнует, что символы табуляции и новой строки во входных данных (если таковые имеются) преобразуются в простые пробелы.
Идея проста: разбить ввод по пробелам и вывести каждый токен, за исключением тех токенов, которые заканчиваются на :
новую строку (и добавить пробел перед остальными). $count
Переменная и связанная с ней if
полезна только для предотвращения начальной пустой строки. Можно удалить, если это не проблема. (Скрипт предполагает, что ввод находится в файле, который называется intput
в текущем каталоге.)
#! /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"
Надеюсь, кто-нибудь сможет предложить более приятную альтернативу.
$ 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