如何用bash中的多个字符将字符串拆分为已定义的字符串?

时间:2018-12-27 23:20:10

标签: bash

以下由几个设备组成的输出需要解析:

 0 interface=ether1 address=172.16.127.2 address4=172.16.127.2
   address6=fe80::ce2d:e0ff:fe00:05 mac-address=CC:2D:E0:00:00:08
   identity="myrouter1" platform="MikroTik" version="6.43.8 (stable)"

 1 interface=ether2 address=10.5.44.100 address4=10.5.44.100
   address6=fe80::ce2d:e0ff:fe00:07 mac-address=CC:2D:E0:00:00:05
   identity="myrouter4" platform="MikroTik" version="6.43.8 (stable)"

 3 interface=ether4 address=fe80::ba69:f4ff:fe00:0017
   address6=fe80::ba69:f4ff:fe00:0017 mac-address=B8:69:F4:00:00:07
   identity="myrouter2" platform="MikroTik" version="6.43.8 (stable)"

...

10 interface=ether5 address=10.26.51.24 address4=10.26.51.24
   address6=fe80::ba69:f4ff:fe00:0039 mac-address=B8:69:F4:00:00:04
   identity="myrouter3" platform="MikroTik" version="6.43.8 (stable)"

11 interface=ether3 address=10.26.51.100 address4=10.26.51.100
   address6=fe80::ce2d:e0ff:fe00:f00 mac-address=CC:2D:E0:00:00:09
   identity="myrouter5" platform="MikroTik" version="6.43.8 (stable)"

编辑:为方便起见,我将输出缩短并匿名化,第一块有7行,第二块有5行,第三块有7行,第四块有4行,因此行数不一致。

基本上是Mikrotik设备的输出:“ /ip neighbor print detail

最佳做法是自己访问每个设备(= number),然后进一步分别访问一个设备的所有setting=value,以最终访问诸如$device[0][identity]之类的设置。

我尝试设置IFS='\d{1,2} ',但似乎IFS仅适用于单字符分隔。

在网络上,我找不到解决此问题的方法,我在寻找错误的方法,还有另一种方法可以解决此问题?

谢谢!

编辑:找到了此解决方案Split file by multiple line breaks,该解决方案帮助我获得了

devices=()
COUNT=0;
while read LINE
do
    [ "$LINE" ] && devices[$COUNT]+="$LINE " || { (( ++COUNT )); }
done < devices.txt

然后我可以使用@Kamil解决方案轻松访问值。

3 个答案:

答案 0 :(得分:1)

虽然您不清楚精确的输出格式,但是bash提供了一种有效的方法,可以使用流程替换解析数据。类似于命令替换进程替换允许将命令输出重定向到stdin。这样一来,您就可以读取将每个设备的mikrotik文件重新格式化为一行的一组命令的结果。

虽然有很多方法可以实现,但要处理将多个设备的多行信息重新格式化为单行所需的多种体操的方法之一是使用tr和{{1 }}。 sed首先用tr替换每个'\n'(或选择您在其他地方未使用的喜欢的字符),然后再次将前导空格“压缩”到单个空格(技术上不需要) ,但出于完整性考虑)。在将'_'替换为'\n'并压缩空格之后,您只需使用两个'_'表达式即可将sed(从空行开始)更改回{{1} },然后删除所有"__"

因此,您可以读取设备号'\n',其余的行则可以插入'_'对。为了轻松找到n行,只需将行转换为数组并使用参数扩展进行循环(用于删除子字符串),您可以将setting=value的值保存并存储为{ {1}}(修剪双引号留给您)

现在,仅是输出值(或对它们执行任何操作)的问题。虽然您可以再次循环并输出数组值,但是很容易将无意引用行传递到"identity=",并让printf-trick处理分隔"identity"对用于输出。最后,形成id标识符,并在设备块的最后一行输出。

完全将其放入,您可以执行以下操作:

printf

使用/输出示例

请注意,脚本将要解析的文件名作为第一个输入。

setting=value

仔细检查一下,如果还有其他问题,请告诉我。正如开头提到的,您尚未定义要查找的显式输出格式,但是要收集问题中包含的信息,这应该很接近。

答案 1 :(得分:0)

  1. 我认为您使用IFS的方向正确。

  2. 尝试通过IFS=$'\n\n'来管道cut(通过接口拆分线组)(以提取每个接口所需的特定字段)。

    < / li>

答案 2 :(得分:0)

Bash喜欢带有分隔线分隔值的单个长行。因此,首先我们需要将您的文件转换为这种格式。

下面,我一次从输入中读取4行。我注意到输出仅跨越4行-我只连接了4行,就好像它是单行一样。

while
    IFS= read -r line1 &&
    IFS= read -r line2 &&
    IFS= read -r line3 &&
    IFS= read -r line4 &&
    line="$line1 $line2 $line3 $line4"
do
    if [ -n "$line4" ]; then
        echo "ERR: 4th line should be empt - $line4 !" >&2
        exit 4
    fi

    if ! num=$(printf "%d" ${line:0:3}); then
        echo "ERR: reading number" >&2
        exit 1
    fi

    line=${line:3}
    # bash variables can't have `-`
    line=${line/mac-address=/mac_address=}

    # unsafe magic
    vars=(interface address address4
        address6 mac_address identity platform version)
    for v in "${vars[@]}"; do
        unset "$v"
        if ! <<<"$line" grep -q "$v="; then
            echo "ERR: line does not have $v= part!" >&2
            exit 1
        fi
    done

    # eval call
    if ! eval "$line"; then
        echo "ERR: eval line=$line" >&2
        exit 1
    fi

    for v in "${vars[@]}"; do
        if [ -z "${!v}" ]; then
            echo "ERR: variable $v was not set in eval!" >&2
            exit 1;
        fi
    done

    echo "$num: $interface $address $address4 $address6 $mac_address $identity $platform $version"


done < file
  • 然后我从行中检索开头的数字,我怀疑该行上印有printf "%3d",所以我只对行${line:0:3}进行了切片
  • 对于其余的行,我缩进使用eval。在这种情况下,我相信上游,但是我尝试断言某些情况(该行中未定义变量,某些语法错误等)
  • 然后神奇的eval "$line"发生了,它分配了我外壳中的所有变量
  • 之后,我可以像常规变量一样使用行中的变量
  • tutorialspoint上的实时示例
  • Eval command and security issues