Как разбить строку на массив в bash

У меня проблема с выходом программы. Мне нужно запустить команду в 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. Я все еще новичок.

6 Solutions collect form web for “Как разбить строку на массив в bash”

Чистое решение 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, поэтому печатается $notfirst , а для последняя строка, добавление дает 2, поэтому печатается пробел. Затем часть после последнего пробела печатается ${f##* } . Колон печатается для всех строк, кроме последнего.

Решение 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++) .

С помощью GNU sed вы можете сопоставить каждую непрерывную строку (т. Е. Без пробелов), завершенную : а затем поместите новую строку перед всеми, кроме первой:

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

Если ваша версия sed не поддерживает расширение gn , вы можете использовать простой g модификатор

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

который будет работать одинаково, за исключением печати новой строки новой строки перед первым ключом. Вы можете использовать perl -pe 's/\S+:/\n$&/g' с той же оговоркой (может быть perl-эквивалент GNU sed g2 но я этого не знаю).

Легче использовать инструмент, который поддерживает образы:

 $ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500" $ grep -oP '\S+:\s+.*?(?=\s+\S+:|$)' <<< "$s" 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=$'\n' foo=($(grep -oP '\S+:\s+.*?(?=\s+\S+:|$)' <<< "$s")) $ for i in "${!foo[@]}"; do echo "$i<==>${foo[i]}"; done 0<==>battery.charge: 90 1<==>battery.charge.low: 30 2<==>battery.runtime: 3690 3<==>battery.voltage: 230.0 4<==>device.mfr: MGE UPS SYSTEMS 5<==>device.model: Pulsar Evolution 500 

EDIT: Объяснение регулярного выражения:

 '\S+:\s+.*?(?=\s+\S+:|$)' 
  • \S+ соответствует одному или нескольким символам без пробелов
  • : совпадения :
  • \s+ соответствует одному или нескольким пробелам после :
  • .*? обозначает неживое соответствие
  • (?=\s+\S+:|$) является исходным утверждением, чтобы определить, есть ли:
    • одно или несколько пробелов, за которыми следует строка (не-пробельные символы) и двоеточие, или
    • конец строки

Таким образом, строка разделяется на части, такие как battery.charge: 90 , … device.mfr: MGE UPS SYSTEMS , …


Ниже приведены ссылки на пару онлайн-анализаторов регулярных выражений:

Вот наивный подход, который должен работать, если вы не заботитесь о том, чтобы вкладки и символы новой строки во входном (если есть) были преобразованы в простые пространства.

Идея проста: разбивайте входные данные на пробелы и печатайте каждый токен, за исключением того, что вы добавляете токены, которые заканчиваются : с помощью новой строки (и снова добавьте пространство перед другими). Переменная $count и related 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 

Вы можете использовать awk (1) со следующим скриптом split.awk:

 BEGIN { RS=" "; first=1; } first { first=0; printf "%s", $1; next; } /[az]+\.[^:]+:/ { printf "\n%s", $1; next; } { printf " %s", $1 } END { printf "\n" } 

Когда вы запускаете

 awk -f split.awk input.dat 

ты получишь

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

Идея состоит в том, чтобы позволить awk разбить вход, когда видит пространство (установка разделителя записей RS в строке 1). Затем он соответствует xxx.yy.zz: значениям в строках 2 и 3 (отличая первое совпадение от последующих), а строка 4 – всякий раз, когда строки 2 и 3 не совпадают. Строка 5 просто напечатает последнюю строку новой строки.

  • Почему звездочка в команде приводит к расширению цикла?
  • Нет подсказки индикатора bash после завершения разветвленного процесса
  • Новая строка в переменных bash
  • Номер команды Bash (история) в PS1
  • Разрешить setuid для сценариев оболочки
  • Как получить две команды?
  • Командная строка SQLite3: как вы отменяете команду?
  • Установка длинной переменной среды ломает много команд
  • Не удалось получить команду для работы с bash -c
  • сценарий оболочки, отредактированный в окнах, отображает сообщение об ошибке
  • Как я могу вывести «временно» в оболочку, как это делает завершение вкладки zsh?
  • Interesting Posts

    Создание виртуальной файловой системы для загрузки Linux

    Как вызываются системные вызовы от человека 2?

    Нет опции «rwnd» для «ip route» для управления окном приема?

    Включение наборов экспортных шифров в Apache / OpenSSL

    fetchmail для регистрации полных сообщений И переход в mda

    назначение привилегий чтения / записи для папки пользователю в CentOS 7

    Как сбросить ветвь git на заданную ранее фиксацию и исправить отдельную головку?

    Будет ли какая-либо проблема с безопасностью, чтобы пользователь, не являющийся пользователем root, настроил loopback-устройства?

    Есть ли какой-либо веб-браузер, который использует keybinding, кроме uzbl?

    Изменить название жесткого диска

    Драйвер ядра для отслеживания на Lenovo Thinkpad 13

    systemd Ошибка при выполнении сценария нереста EXEC: разрешение отклонено

    Горизонтальная прокрутка с меньшими приращениями с меньшим значением -S

    Обновления системы в архиве linux с / boot в отдельном разделе?

    Каково максимальное количество x-клиентов?

    Linux и Unix - лучшая ОС в мире.