【问题标题】:bash function pass space & line separated arguments from sedbash 函数从 sed 传递空格和行分隔的参数
【发布时间】:2017-03-21 03:41:06
【问题描述】:

在我的情况下,如何将参数传递给 bash 中的函数或循环?字段分隔符将是空格和下一行(\n)。目标是从日志文件中获取所有路由命令,然后重新应用相同的命令,但在应用之前将其删除。

文件.log

Mon Nov  7 10:55:28 2016 bla bla bla
Mon Nov  7 10:55:28 2016 bla blabla bla
Mon Nov  7 11:19:27 2016 /sbin/route add -net 123.123.123.0 123.123.123.104 255.255.240.0
Mon Nov  7 11:19:27 bla bla bla
Mon Nov  7 11:19:27 2016 /sbin/route add -net 111.111.111.111 192.168.1.1 255.255.255.255
Mon Nov  7 11:19:32 2016 /sbin/route add -net 4.4.2.2 224.224.224.1 255.255.255.255
Mon Nov  7 11:19:32 bla blabla bla
Mon Nov  7 11:19:32 2016 /sbin/route add -net 222.222.222.222 224.224.224.1 255.255.255.255
Mon Nov  7 11:20:21 bla blabla bla
Mon Nov  7 11:20:21 bla bla bla
Mon Nov  7 11:20:23 bla bla bla

以下是从日志文件中提取路由命令添加,

sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' /path/to/file.log | awk '!seen[$0]++'

这是我正在尝试的 bash 函数,

#!/bin/bash

add_routes() {

$1 delete $3 $4 $5 $6      #expecting: eg: /sbin/route delete -net 4.4.2.2 224.224.224.1 255.255.255.255
$@                         #expecting: eg: /sbin/route add -net 4.4.2.2 224.224.224.1 255.255.255.255


}

add_routes $(sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' /path/to/file.log | awk '!seen[$0]++')

从上述函数中按顺序执行所需的输出命令(先删除以应用从日志中提取的最新路由网络),

/sbin/route delete -net 123.123.123.0 123.123.123.104 255.255.240.0
/sbin/route add -net 123.123.123.0 123.123.123.104 255.255.240.0
/sbin/route delete -net 111.111.111.111 192.168.1.1 255.255.255.255
/sbin/route add -net 111.111.111.111 192.168.1.1 255.255.255.255
/sbin/route delete -net 4.4.2.2 224.224.224.1 255.255.255.255
/sbin/route add -net 4.4.2.2 224.224.224.1 255.255.255.255
/sbin/route delete -net 222.222.222.222 224.224.224.1 255.255.255.255
/sbin/route add -net 222.222.222.222 224.224.224.1 255.255.255.255

从上述函数中,$@ 执行将所有路由命令粘贴在一行中,但失败。并且删除命令(高于 $@ 行)只删除第一条路线。

如果完成工作,任何人都知道如何通过函数或for loop 获得所需的输出?谢谢!

【问题讨论】:

    标签: bash macos function for-loop sed


    【解决方案1】:

    不用sed,您也可以在 BASH 中执行此操作:

    while read -r; do
       read -ra arr <<< "/sbin/${REPLY#* /sbin/}" # strips anything before /sbin/
       [[ ${arr[0]} == /sbin/route ]] &&
       "${arr[@]/add/delete}" &&
       "${arr[@]}"
    done < file.log
    

    编辑:

    这里有一个 awk 命令可以完成所有操作:

    awk '{sub(/^([^[:blank:]]+[[:blank:]]+){5}/, "")} $1 == "/sbin/route" && !seen[$0]++{
          s=$0; sub(/add/, "delete"); print; print s}' file.log | bash
    

    【讨论】:

    • 糟糕,在实际的日志文件中,它不是以 route 命令开头,而是以一些日期和时间为前缀。没有完全安排file.log是我的错误。顺便说一句,我也在使用 sed 来避免重复的路线。我真的不知道我们可以只使用 bash,很高兴知道新事物。想知道您是否可以编辑以便它读取路由命令,即使它不是在一行中首先开始...
    • 更新了我的问题!
    • 不错,与 awk 相比,sort 的小问题是我丢失了它必须执行的序列
    • 你可以用你的 awk 命令替换我的grep|sort 命令。如果它不起作用,请告诉我。
    • 感谢@EdMorton,我已经编辑了 awk 命令以与非 gnu awk 一起使用。我还拿出了getline
    【解决方案2】:

    直接将 sed 命令的输出传递给函数的问题是,正如您所见,输出以空格分隔(我认为)。

    有时您会看到人们通过玩 IFS 和 OFS 游戏来解决这个问题。我总是把它搞砸,并认为它看起来很乱。

    我更喜欢这种循环方式,因为它往往会按我的预期工作。如果您在文件上运行 for 循环,它可以将行中的空格解释为换行符并且事情变得混乱。 http://www.compciv.org/topics/bash/loops/ 解释了这一点,值得一读。

    while read x; do ... done &lt; &lt;(cmd) 样式循环:

    while read log_line;do
    
        add_routes ${log_line}
    
    done < <(sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' file.log)
    

    基本上,您将sed 的输出重定向到fifo,然后将fifo 重定向到read 的stdin。 read 足够聪明,可以将 sed 的单行输出读入 log_line 变量。

    现在log_line 包含一行输出,您只需将其传递给您编写的 add_routes 函数即可。

    如果您不想解释换行符,有时有必要执行read -r,但显然在这里您可以这样做!

    举个完整的例子……

    脚本.sh

    #!/bin/bash
    
    add_routes(){
        echo "$1 delete $3 $4 $5 $6";
        echo $@;
    }
    
    while read thing;do
    
        add_routes ${thing}
    
    done < <(sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' file.log)
    

    file.log 就是你所列出的。

    输出:

    $ ./script.sh
    /sbin/route delete -net 123.123.123.0 123.123.123.104 255.255.240.0
    /sbin/route add -net 123.123.123.0 123.123.123.104 255.255.240.0
    /sbin/route delete -net 111.111.111.111 192.168.1.1 255.255.255.255
    /sbin/route add -net 111.111.111.111 192.168.1.1 255.255.255.255
    /sbin/route delete -net 4.4.2.2 224.224.224.1 255.255.255.255
    /sbin/route add -net 4.4.2.2 224.224.224.1 255.255.255.255
    /sbin/route delete -net 222.222.222.222 224.224.224.1 255.255.255.255
    /sbin/route add -net 222.222.222.222 224.224.224.1 255.255.255.255
    

    请注意,我正在回显您想要运行的命令,因为我实际上并不想弄乱我的路线。

    有关 bash 重定向的更多信息,请查看http://www.catonmat.net/download/bash-redirections-cheat-sheet.pdf

    【讨论】:

    • 好消息!。我正在测试这个,我会回来告诉你。感谢您的关注!
    • 你值得赏金,现在不让我做!
    • 您可能想先阅读 unix.stackexchange.com/questions/169716/… 和 google bash 基础知识。 read -r 与解释换行无关。
    • 读起来很有趣。错误解释了读取 -r 的手册页,感谢您指出。我会考虑使用 xargs 来避免将来不必要的循环。
    【解决方案3】:

    add_routes() 被写入仅处理一个路由时,您将sed 的多行输出一次性传递给add_routes()

    在不重写整个代码的情况下,直接回答您的问题是使用 xargs 可以处理多行并一次传递一行(或 6 个参数)传递给 add_routes()

    你只需要改变这一行:

    add_routes $(sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' /path/to/file.log)
    

    到这一行:

    sed -n 's/^.*\(\/sbin\/route\ add \-net\)/\1/p' /path/to/file.log | xargs -n6 add_routes
    

    -n 6 告诉xargs 一次传递6 参数。

    祝你好运。

    【讨论】:

      【解决方案4】:
      $ cat tst.sh
      awk 'sub(".*/sbin/route add ","") {
          print "delete", $0
          print "add", $0
      }' file |
      xargs -d $'\n' -n 1 echo /sbin/route
      
      $ ./tst.sh
      /sbin/route delete -net 123.123.123.0 123.123.123.104 255.255.240.0
      /sbin/route add -net 123.123.123.0 123.123.123.104 255.255.240.0
      /sbin/route delete -net 111.111.111.111 192.168.1.1 255.255.255.255
      /sbin/route add -net 111.111.111.111 192.168.1.1 255.255.255.255
      /sbin/route delete -net 4.4.2.2 224.224.224.1 255.255.255.255
      /sbin/route add -net 4.4.2.2 224.224.224.1 255.255.255.255
      /sbin/route delete -net 222.222.222.222 224.224.224.1 255.255.255.255
      /sbin/route add -net 222.222.222.222 224.224.224.1 255.255.255.255
      

      当您对命令行感到满意时,只需删除 echo

      如果您没有 GNU xargs,则 -d 参数可能不可用,然后您需要改用它(未经测试):

      awk -v ORS='\0' 'sub(".*/sbin/route add ","") {
          print "delete", $0
          print "add", $0
      }' file |
      xargs -0 -n 1 echo /sbin/route
      

      【讨论】:

      • xargs: illegal option -- d usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr] [-L number] [-n number [-x]] [-P maxprocs] [-s size] [utility [argument ...]] - 仅供参考,我在 OSX 上
      • -d 一定是 GNU 主义,抱歉我不知道。你能安装 GNU xargs 吗?无论如何,我只是通过在 awk 命令的开头添加 -v ORS='\0' 并在 xargs 命令中将 -d $'\n' 替换为 -0 来添加一个适用于任何 xargs 的版本。
      • 不走运,也许我应该考虑安装 gnu-xargs。我试过你更新的 awk 部分,所有路由都粘贴在一行中,/sbin/route 仅添加到第一次删除,所有其他的后面都没有 /sbin/route。
      • 我将不得不在多个 OSX 节点上使用它作为路径,并且主要避免额外安装,并且只是尝试使用开箱即用的解决方案,因为可能找不到 brew 并且再次进行 brew 安装的额外工作,如果已经安装然后权限问题待处理!。
      • 我的意思是补丁而不是上面评论中的路径!
      【解决方案5】:

      这可能对你有用(GNU sed):

      sed -nr '\#^.*(/sbin/route add )#{s//\1/;h;G;s/add/delete/ep}' file
      

      这会使用模式匹配过滤文件,然后复制所需的输入并将第一个 add 替换为 delete。然后评估输出。

      这也应该适用于大多数 sed 和 shell;

      sed -n '\#^.*\(/sbin/route add \)#{s//\1/;h;G;s/add/delete/p}' file |
      shell
      

      【讨论】:

      • sed: illegal option -- r usage: sed script [-Ealn] [-i extension] [file ...] sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...] - 也许我应该考虑在我的 OSX 中使用 gnu-sed,你已经提到过。我将不得不在多个 OSX 节点上使用它,并且主要避免额外的安装,而只是尝试使用开箱即用的解决方案,因为可能找不到 brew 并且再次进行额外的工作!让我在其他情况下考虑这一点。
      猜你喜欢
      • 2017-05-18
      • 2017-10-28
      • 2017-09-04
      • 1970-01-01
      • 2011-11-24
      • 2023-03-18
      • 2017-08-19
      • 2021-10-05
      相关资源
      最近更新 更多