Мне нужно преобразовать файл, который выглядит так:
Fri Apr 14 15:42:02 UTC 2023
MemTotal: 65039504 kB
MemFree: 41010436 kB
MemAvailable: 45100588 kB
Fri Apr 14 16:35:01 UTC 2023
MemTotal: 65039504 kB
MemFree: 40409508 kB
MemAvailable: 44902852 kB
Fri Apr 14 16:36:01 UTC 2023
MemTotal: 65039504 kB
MemFree: 40411232 kB
MemAvailable: 44905376 kB
Что-то похожее на это:
15:42:02,65039504,41010436,45100588
16:35:01,65039504,40409508,44902852
16:36:01,65039504,40411232,44905376
Вот сценарий, который я придумал:
#!/bin/bash
set -x
export TIME
export MEMTOTAL
export MEMFREE
export MEMAVAIL
export FILE = "./SMALL-SAMPLE.txt"
while read -a LINE from $FILE
do
WORD1=${LINE[0]}
WORD2=${LINE[1]}
WORD3=${LINE[2]}
WORD4=${LINE[3]}
WORD5=${LINE[4]}
WORD6=${LINE[5]}
WORD7=${LINE[6]}
case $WORD1 in
"Fri")
TIME=$WORD4
;;
"MemTotal")
MEMTOTAL=$WORD2
;;
"MemFree")
MEMFREE=$WORD2
;;
"MemAvailable")
MEMAVAIL=$WORD2
;;
*)continue;;
esac
LINEOUT = "$TIME,$MEMTOTAL,$MEMFREE,$MEMAVAIL"
echo $LINEOUT
done < $FILE
Вот результат:
15:42:02,,,
16:35:01,,,
16:36:01,,,
У меня где-то в этом скрипте спрятана ошибка новичка... есть идеи, почему я не могу получить свои данные?
Ты забыл все :
. $WORD1
это MemTotal:
, а не MemTotal
Так что "MemTotal")
должно быть "MemTotal:")
Вы должны печатать выходные строки только после того, как прочитаете строки MemAvailable
. Вы печатаете его после прочтения каждой входной строки, поэтому вы перепутаете переменные из разных групп.
perl -ne '
if (/:[^ ]/) { print /\d+:\d+:\d+/g, "," }
elsif (/: /) { print /(\d+)/, $. % 6 == 5 ? "\n" : "," }
' -- file
-n
читает ввод построчно и запускает код для каждой строки;Один из подходов:
Удалить все пустые строки (с sed '/^$/d' $FILE
)
Прочитайте четыре строки сразу на каждой итерации (повторяя read
для каждой)
Извлеките нужное поле с помощью команды cut
$ cat script.sh
#!/bin/bash
FILE = "./SMALL-SAMPLE.txt"
while read ltime; \
read lmemt; \
read lmemf; \
read lmema;
do
TIME=$(echo $ltime | cut -d ' ' -f 4)
MEMT=$(echo $lmemt | cut -d ' ' -f 2)
MEMF=$(echo $lmemf | cut -d ' ' -f 2)
MEMA=$(echo $lmema | cut -d ' ' -f 2)
echo "$TIME,$MEMT,$MEMF,$MEMA"
done < <(sed '/^$/d' $FILE)
Тестирование:
$ ./script.sh
15:42:02,65039504,41010436,45100588
16:35:01,65039504,40409508,44902852
16:36:01,65039504,40411232,44905376
Потому что TMTOWTDI, более короткая версия Perl:
<file perl -anE '
push @a, $F[3]||$F[1]||();
say join",",splice@a if @a>3;
'
-n
заставляет Perl запускать скрипт для каждой записи/строки.-a
включает авторазбиение записей (по пробелам) на массив @F
@a
первое определенное из $F[3]
(отметка времени) или $F[1]
(значение). Если ни один из них не определен, добавление ()
недопустимо.@a
имеет 4 элемента, выведите их и усеките массив.В вашем коде есть несколько ошибок и стилистических проблем.
*
/continue
)read ... from $FILE
означает не то, что вы думаете.file = "./SMALL-SAMPLE.txt"
while read -a line
do
case "${line[0]}" in
Fri)
time=${line[3]}
;;
MemTotal:)
memtotal=${line[1]}
;;
MemFree:)
memfree=${line[1]}
;;
MemAvailable:)
memavail=${line[1]}
echo "$time,$memtotal,$memfree,$memavail"
;;
esac
done < "$file"
еще короче perl: perl -anE '$s = $F[3]||"$s,$F[1]" if @F; say $s if /Avail/'
еще короче awk: awk 'NF { s = NF>3 ? $4 : s","$2 } /Avail/ {print s}'
Мне очень нравится, как вы perl
ребята можете делать такие маленькие скрипты. Также хорошо, что вам удалось заставить скрипт OP работать без серьезных изменений. Я думаю, что условие Fri
подвержено ошибкам. Кажется, это означает «пятница», поэтому в другие дни это, вероятно, не сработает.
Вы забыли завершающие двоеточия в своем выборе case
("MemTotal:"
вместо "MemTotal"
). Но есть гораздо более простые и быстрые решения (циклы bash медленные).
Пример с awk
(протестировано с GNU awk
и awk
, поставляемым с macOS Ventura):
$ awk -v RS= -v OFS=, '/^Mem/ {print t,$2,$5,$8;next} {t=$4}' file
15:42:02,65039504,41010436,45100588
16:35:01,65039504,40409508,44902852
16:36:01,65039504,40411232,44905376
Пояснения:
-v RS=
устанавливает разделитель записей на пустые строки.-v OFS=,
устанавливает разделитель полей вывода на запятые./^Mem/ {print t,$2,$5,$8;next}
применяется к записям, начинающимся с Mem
, печатает значение переменной t
и полей 2, 5 и 8 (3 размера в записи) и переходит к следующей записи.{t=$4}
хранит четвертое поле (время) в переменной t
.echo 'Fri Apr 14 15:42:02 UTC 2023
MemTotal: 65039504 kB
MemFree: 41010436 kB
MemAvailable: 45100588 kB
Fri Apr 14 16:35:01 UTC 2023
MemTotal: 65039504 kB
MemFree: 40409508 kB
MemAvailable: 44902852 kB
Fri Apr 14 16:36:01 UTC 2023
MemTotal: 65039504 kB
MemFree: 40411232 kB
MemAvailable: 44905376 kB' |
nawk '(NF = NF)^(ORS = NR % 4 ? "," : "\n")' RS='\n+' \ OFS= FS='^(([^: ]+ )+|[^ :]+: )| [[:alpha:]]+.+$'
gawk '(ORS=/v/?RS:",")^!(NF+=OFS=_)' FS='^([^:]+|[^ ]+) | [?-|].+$'
mawk 'NF&&/v/*($__=_=/: /?_","$2:$4)'
15:42:02,65039504,41010436,45100588
16:35:01,65039504,40409508,44902852
16:36:01,65039504,40411232,44905376
Почему бы и нет
while read WORD1 WORD2 WORD3 ...
?