Как разделить строку на массив в ударе

Наличие "среднего числа загрузки на пользователя" не имеет большого смысла, так как среднее число загрузки только имеет смысл в контексте большей системы. Можно, однако, получить информацию использования, которая могла бы быть тем, что Вы хотите. sa от psacct может сделать это, например:

На приложение

# sa       
  223730   62617.91re      31.57cp         0avio      5583k
     284    1515.73re      10.45cp         0avio     46067k   perl
     314      17.53re       8.90cp         0avio     65746k   redis-server*
     152    1779.02re       6.46cp         0avio     56377k   vim
       8    2503.01re       1.69cp         0avio      6089k   snort*
   11209      99.64re       1.52cp         0avio      6100k   git
   20269      33.24re       0.48cp         0avio     30273k   postgres*
     140       0.48re       0.37cp         0avio      1831k   sort
       3    1193.10re       0.26cp         0avio    128389k   pipe_errorlog.c
       7    3146.93re       0.20cp         0avio    156983k   apache2*
      26       0.16re       0.15cp         0avio     32113k   ack
      19    2692.45re       0.12cp         0avio     14226k   sshd
      10       0.22re       0.11cp         0avio     14464k   apt-show-versio
      25       5.58re       0.08cp         0avio      7042k   apt-get
     118       0.40re       0.08cp         0avio      9674k   find
       4       1.51re       0.08cp         0avio      1212k   rkhunter
     377       0.33re       0.07cp         0avio     14768k   rm
     226    2696.40re       0.05cp         0avio     11524k   bash

На пользователя

# sa -m
root                               201825   17279.57re      13.26cp         0avio      2981k
110                                   255      14.86re       7.61cp         0avio     65544k
nobody                                222    4659.53re       7.24cp         0avio     38149k
snort                                   1    1440.00re       1.60cp         0avio     25792k
108                                   763     798.83re       1.24cp         0avio      6964k
105                                 12801      31.79re       0.47cp         0avio     33102k
114                                    94    2741.65re       0.09cp         0avio      9906k
109                                     7   16944.24re       0.02cp         0avio     25483k
Debian-exim                          3772       0.81re       0.00cp         0avio     25086k
man                                   182       0.02re       0.00cp         0avio      5203k
116                                    11     796.78re       0.00cp         0avio      9823k
107                                    10     795.09re       0.00cp         0avio      9823k
statd                                3690       0.68re       0.00cp         0avio     25532k
sshd                                   22       0.44re       0.00cp         0avio     12690k
www-data                                3       0.00re       0.00cp         0avio     11603k
111                                     2   17112.17re       0.00cp         0avio     19048k
mail                                    1       0.00re       0.00cp         0avio     11184k
6
14.04.2019, 11:52
7 ответов

Чистое решение на 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 и $ двоеточие содержат различные строки, разделяющие поля: в первом элементе notfirst + last равно 0, поэтому ничего не добавляется в начале, для остальных из строк $ notfirst равно 1, поэтому печатается новая строка, а для последней строки добавление дает 2, поэтому печатается пробел. Затем печатается часть после последнего пробела $ {f ## *} . Двоеточие печатается для всех строк, кроме последней.

2
27.01.2020, 20:23
[1123623]Решение [1123996]perl[1123997]:

dd if=/dev/sda bs=512 count=63 | hexdump -C

Объяснение

\S+:[1124455] совпадает с концом строки на [1124456]:[1124457].

Со всеми совпадающими строками мы вставляем перед ними новую строку [1124458]("\n$&")[1124459] кроме первой [1124460]($seen++)[1124461].

3
27.01.2020, 20:23

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

BEGIN { RS=" "; first=1; }
first { first=0; printf "%s", $1; next; }
/[a-z]+\.[^:]+:/ { 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 просто выводит последнюю новую строку.

1
27.01.2020, 20:23

Проще использовать инструмент, поддерживающий поисковые запросы:

$ 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

РЕДАКТИРОВАТЬ: Объяснение регулярного выражения:

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

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


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

2
27.01.2020, 20:23

С 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 , но я не знаю).

3
27.01.2020, 20:23

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

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

Вот короткий awkскрипт для демонстрации awkмощности.

awk '
len=patsplit($0, namesArr,"[^ :]+: ", valuesArr) {
    for(i=0;i<=len;i++)
        print namesArr[i], valuesArr[i]
}' input.txt

input.txt

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

выход:

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

Теги

Похожие вопросы