【问题标题】:Extract next word in a space-delimited string提取以空格分隔的字符串中的下一个单词
【发布时间】:2020-09-02 02:33:35
【问题描述】:

这似乎是一个非常受欢迎的问题,但由于某种原因,我在这里找到的我正在尝试的 awk 解决方案不起作用。

我有一个别名,可以获取我网络上某些设备的信息。我希望能够搜索列表并检索设备“名称”。别名和输出如下所示:

$ mtb devices
Alias     Address           Name         Software   Brand
------------------------------------------------------------
          192.168.1.107     Device1      1009       Some_brand
TV        192.168.1.103     Device2      0831       Some_other_brand
TV2       192.168.1.105     Device3      0831       Some_brand

在我的脚本中,使用 grep 我可以找到我正在寻找的行,因为我通过 ip 地址进行搜索:

IP=$1
BOX=`mtb devices | grep ${IP}`
echo $BOX

这给了我一个结果:

$ ./my_devices.sh 192.168.1.105
TV2 192.168.1.105 Device3 0831 Some_brand

现在我想从该行中获取“名称”,所以我使用了 awk '{print $3}':

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{print $3;}'`
echo $BOX
$ ./my_devices.sh 192.168.1.105
Device3

但是当我搜索一个没有别名的IP地址时,我显然得到了错误的结果:

$ ./my_devices.sh 192.168.1.107
1009

我寻找了更多的解决方案,我找到了另一个 awk 解决方案,但是新的 awk 不起作用:

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{ for(i=1;i<=NF;i++) if ($i == "$IP") print $(i+1) }'`
echo $BOX

当我运行我的脚本时,它什么也不返回:

$ ./my_devices.sh 192.168.1.105
$

显然我希望它返回Device3。

IP 地址字段和名称字段将始终被填充,但别名字段可能不会被填充。

【问题讨论】:

  • 1) 代替波浪号,使用以下语法:BOX=$( command )。 2) 如果您按预期传递变量,您的最后一个 awk 将起作用:mtb devices | awk -v ip="$IP" '{ for(i=1;i&lt;=NF;i++) if ($i == ip) print $(i+1) }'(也省略 grep,只有一个命令就足够了,grep 或 awk 或 sed 等)
  • 我从未听说过mtb,但是大多数可以在人类可读的表格中生成输出的工具也可以选择以更容易机器可读的格式生成输出 - 你检查过mtb 的手册页,看看它是否有这样的选项?

标签: linux bash shell awk


【解决方案1】:

如果一个观察结果适用于来自 mtb devices 的所有输出,则设备名称可以引用为
(字段数)- 2

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{ print $(NF - 2); }'`
echo $BOX

或缩短的替代:

mtb devices | awk "/$1/ { print \$(NF - 2); }"

【讨论】:

  • 喜欢这个,但$IP 周围应该有空格,这样 1.2.3.4 就不会同时接收 1.2.3.40...
【解决方案2】:

如果你不喜欢awk,总有sed:

mtb devices | sed -ne "s/.*192.168.1.107\s*\(\w\+\).*/\1/p" 
# or
mtb devices | sed -ne "s/.*$IP\s*\(\w\+\).*/\1/p" 

这将-not 打印任何行,除非-expression 另有说明。我们的表达式是s//p,它执行s/ubstitution,/prints 结果。

如果我们进行替换,我们抓取.*whole line.*,在我们的变量之后捕捉下一个\word,然后用我们捕捉到的\1 组替换整个抓取的行。

【讨论】:

    【解决方案3】:

    通常,grep 很棒,但是当我需要进行稍微复杂的匹配和裁剪时,我会转向 sed 以避免产生不必要的进程。

    mtb devices | sed -En "/ $IP /{ s/^.* $IP +(\\S+) .*/\\1/; p; }" 
    

    不需要awk。 / $IP / 替换了 grep,s/^.* $IP +(\\S+) .*/\\1/ 将其他所有内容从行中剥离,留下 IP 之后的字段。 -n 除非被询问,否则会阻止打印,p 会询问。 -E 让我不必使用大量的反斜杠引用,但由于我嵌入了 $IP,我仍然需要在 \\1 中添加一个额外的 \。

    我实际上是用\s 和\S 编写的,它可以工作,并且没有额外的\,但我正在仿真。在您的环境中进行测试。

    【讨论】:

      【解决方案4】:

      第一个解决方案认为您要打印下一个字段是否找到 IP 地址。第二种解决方案查找 Input_file 的标题并检查哪个字段中包含字符串 Name 然后打印它,因此这将更加通用,因为即使 Name 存在于它将打印它的任何字段中。

      第一种解决方案:您能否尝试以下操作。需要通过将 ip 地址作为参数提供给脚本来运行此脚本。

      cat script.ksh
      arg="$1"
      mtb devices | 
      awk -v ip="$arg" '
      {
        for(j=1;j<=NF;j++){
          if(j==ip){
             print $(j+1)
          }
        }
      }'
      


      第二个解决方案(通用解决方案): 或者,如果您想从第一行获取 Name 字段列号,那么以下可能会对您有所帮助。

      cat script.ksh
      arg="$1"
      mtb devices |
      awk -v ip="$arg" '
      FNR==1{
        for(j=1;j<=NF;j++){
          if($j=="Name"){
             field=j
          }
        }
      }
      {
        for(j=1;j<=NF;j++){
          if(j==ip){
             print $field
          }
        }
      }'
      

      【讨论】:

        【解决方案5】:

        简答:

        #!/bin/bash
        
        if [ $# -ne 1 ]; then
           read -p "enter ip: " ip
        fi
        
        awk -v ip="$ip" "/ip/{if (NF==5) {print $3}}" <<< $(mtb devices)
        

        DR;TL

        对于 IP (192.168.1.107) 没有名称,您可以非常轻松地检查字段计数 在 awk 中使用名为 “NF”的特殊变量

        mtb devices | grep '192\.168.\1\.107' | awk '{if (NF==5) {print $3}}'
        

        这不会打印任何东西。

        mtb devices | grep '192\.168\.1\.103' | awk '{if (NF==5) {print $3}}'
        

        这将打印 Device2

        注意:使用正则表达式时,请记住点字符 (.) 代表任何字符,最好将其转义,如:\.

        【讨论】:

          猜你喜欢
          • 2010-12-01
          • 2019-08-10
          • 1970-01-01
          • 1970-01-01
          • 2023-03-08
          • 1970-01-01
          • 2017-12-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多