【问题标题】:Parse rsync stats, line Number of files with bash onlyParse rsync stats, line Number of files with bash only
【发布时间】:2020-08-05 15:01:03
【问题描述】:

我需要解析 rsync 统计信息,例如:

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 rsync Output
但我不想依赖 perl,而 Dan Lowe 的答案并非在所有情况下都有效,因为 () 中的内容可能是 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

感谢您的帮助

【问题讨论】:

    标签: regex bash parsing grep rsync


    【解决方案1】:

    使用 Grep 的纯 Bash

    我认为没有理由避免使用 Perl,它在文本解析方面非常方便。但这是一个纯 Bash 实现,它从包含 rsync 统计输出的 rawstats 变量中生成关联数组 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
    )
    

    进程替换 (&lt;( ... )) 允许在循环中使用 stats 变量。管道会创建不共享变量的子外壳。

    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)' \
    )
    

    【讨论】:

      【解决方案2】:

      在每个 UNIX 机器上的任何 shell 中使用任何 awk:

      $ 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 关联数组。

      【讨论】:

        【解决方案3】:

        看起来您的要求已更改...(特定于 gawk)zalem.awk:

        BEGIN {
          FS="[(),:]"
        }
        /^Number of files:/ {
          for(i=2;i<NF;i++)
            printf("[%d]=%s\n", i-1, gensub(/[[:space:]]/, "","g",$i))
        }
        

        【讨论】:

          【解决方案4】:

          类似的东西 - 有点冗长,但是.. 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)
          

          【讨论】:

            猜你喜欢
            • 2023-02-20
            • 2018-11-06
            • 2020-11-12
            • 1970-01-01
            • 2012-01-04
            • 2019-01-10
            • 2022-12-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多