Анализировать rsyn c stats, строка Количество файлов только с bash - PullRequest
0 голосов
/ 05 августа 2020

Мне нужно проанализировать статистику rsyn c, например:

Number of files: 265 (reg: 189, dir: 10, link: 66)
Number of created files: 18
Number of deleted files: 4
Number of regular files transferred: 24
Total file size: 121.67K bytes
Total transferred file size: 0 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 9.15K
Total bytes received: 33

sent 9.15K bytes received 33 bytes 18.37K bytes/sec
total size is 121.67K speedup is 13.24

Анализировать каждую строку довольно легко с помощью таких команд:

$(echo "$rawstats" | grep -Po '(?<=Number of files: ).*')

Теперь мне нужно проанализировать первую линия. Я нашел здесь решение Perl: Perl Parse rsyn c Output но я не хочу полагаться на perl, и ответ Дэна Лоу не будет работать во всех случаях, поскольку в () может быть любая комбинация reg :, dir :, link: (и даже другие, которые я игнорирую). Т.е.:

265 (reg: 189, dir: 10, link: 66)
265 (dir: 10, link: 66)
265 (link: 66)

Итак, я пытаюсь создать правильное регулярное выражение для перехода к grep -P Пока что я нашел:

(\d+) \((?:([a-z]+): (\d+)(?:, )?)*\)?

Что соответствует этому:

[0] is a null string
[1]=265
[2]=link
[3]=66

Результат, которого я ожидал:

[1]=265
[2]=reg
[3]=189
[4]=dir
[5]=10
[6]=link
[7]=66

Я не вижу, как улучшить свой результат. Даже лучшим результатом будет ассоциативный массив bash, например:

[reg]=189
[dir]=10
[link]=66

Спасибо за вашу помощь

Ответы [ 4 ]

2 голосов
/ 06 августа 2020

Использование любого awk в любой оболочке в каждом поле UNIX:

$ cat tst.awk
BEGIN { FS="[(): ,]+" }
sub(/^Number of files: [0-9]+ /,"") {
    for (i=2; i<NF; i+=2) {
        printf "[%s]=%d\n", $i, $(i+1)
    }
    exit
}

$ awk -f tst.awk file
[reg]=189
[dir]=10
[link]=66

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

1 голос
/ 05 августа 2020

Pure Bash with Grep

Не вижу причин избегать Perl, что довольно удобно, когда дело касается парсинга текста. Но вот чистая реализация Bash, которая создает ассоциативный массив stats из rawstats переменной, содержащий вывод rsyn c stats:

declare -A stats=()

label_regex='Number of files:'
num_of_files_line=$(grep -E "$label_regex" <<< "$rawstats")

regex="$label_regex ([0-9]+)"
[[ $num_of_files_line =~ $regex ]] && stats['total']=${BASH_REMATCH[1]}

while read -r k v; do stats["$k"]="$v"; done < <( \
    regex='([a-z]+): ([0-9]+)'
    while [[ $num_of_files_line =~ $regex ]]; do
        match=${BASH_REMATCH[0]}
        printf "%s %s\n" "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
        num_of_files_line=${num_of_files_line#*"$match"}
    done
)

Замена процесса (<( ... )) позволяет используйте переменную stats в l oop. Каналы будут создавать суб-оболочки, которые не разделяют переменные.

Perl

А вот аналогичная реализация Perl, которую я, вероятно, использовал бы:

declare -A stats=()
while read -r k v; do stats["$k"]="$v"; done < <( \
  printf "%s\n" "$rawstats" | \
    perl -ne '/Number of files: (\d+)/ or next; print "total $1\n"; print "$1 $2\n" while (/([a-z]+): (\d+)/g)' \
)
0 голосов
/ 05 августа 2020

похоже, что ваше требование изменилось ... (gawk specific c) zalem.awk:

BEGIN {
  FS="[(),:]"
}
/^Number of files:/ {
  for(i=2;i<NF;i++)
    printf("[%d]=%s\n", i-1, gensub(/[[:space:]]/, "","g",$i))
}
0 голосов
/ 05 августа 2020

что-то в этом роде - немного многословно, но .. echo "$rawstats" | awk -f zalem.awk где zalem.awk:

BEGIN {
  FS="[()]"
}
/^Number of files:/ {
  np=split($2, npA,/, */)
  gsub(/[^0-9]/,"",$1)
  for(i=1;i<=np;i++) {
     printf("%s (", $1)
     for(j=i;j<=np;j++)
        printf("%s%s%s", (j==i)?"":" ", npA[j], (j==np)?")"ORS:",")
  }
}

дает:

 265  (reg: 189, dir: 10, link: 66)
 265  (dir: 10, link: 66)
 265  (link: 66)
...