【问题标题】:grep a block of lines into a shell variable based on matching pattern根据匹配模式将一行行grep到shell变量中
【发布时间】:2019-12-06 18:07:10
【问题描述】:

我正在寻找一种方法来根据给定的模式 grep {} 之间的行块。我尝试了在谷歌中找到的各种模式,但没有一个对我的情况有帮助。我不是 regex.looking 专家寻求一些帮助来解决这个问题。 这是示例源文件:

Data {
        status 400;
        server_name test.dummy.com;

        location /test {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9201.com;

               proxy_http_version 1.1;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection "upgrade";
        }

        location /dev {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9202.com;

               proxy_http_version 1.1;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection "upgrade";
        }

        location /prd {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9203.com;

               proxy_http_version 1.1;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection "upgrade";
        }
}

如果传递给脚本的参数是 "dev" ,那么它应该匹配模式 location /dev 并将下面的块提取到 shell 变量中:

位置 /dev {

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9202.com;
               proxy_http_version 1.1;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection "upgrade";

我尝试了各种 sed/awk 命令模式,但下面的这个给出了一些最接近的结果。

awk '/dev/{print}' RS={ FS=} test.conf

结果:

$ awk '/dev/{print}' RS={ FS=} test.txt

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9201.com;

               proxy_http_version 1.1;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection "upgrade";
        }

        location /dev

【问题讨论】:

  • 在 SO 上,我们确实鼓励人们加入他们为解决自己的问题而付出的努力,您能否在您的问题中添加相同的内容,然后让我们知道。

标签: linux shell unix awk sed


【解决方案1】:

我们可以在单个awk 中完成,请您尝试关注。

awk '/}/ && found{exit} /location \/dev/{found=1;next} found && NF' Input_file

由于 OP 提到应该只打印第一组,所以我在这里使用exit,一旦打印出第一组就立即退出。

说明:为上述代码添加说明。

awk '                   ##Starting awk program from here.
/}/ && found{           ##Checking condition if a line contains } AND variable found is SET then do following.
  exit                  ##Exiting from program here.
}                       ##Closing BLOCK for above condition here.
/location \/dev/{       ##Checking condition here if a line contains location /dev then do following.
  found=1               ##Setting variable found to 1 here.   
  next                  ##next will skip all further statements from here.
}                       ##Closing BLOCK for above condition here.
found && NF             ##Checking condition is  found is SET and NF is NOT NULL then print current line.
' Input_file            ##Mentioning Input_file name here.

输出如下。

proxy_set_header X-Forwarded-Host ;
proxy_set_header X-Forwarded-Server ;
proxy_set_header X-Forwarded-For ;
proxy_set_header Host ;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9202.com;
               proxy_http_version 1.1;
               proxy_set_header Upgrade ;
               proxy_set_header Connection "upgrade";

【讨论】:

  • 它有效。但它并不严格匹配模式(我同意我应该更具体)。如果我使用 /dev2 在文件中再添加一节。它给出了两个结果。
  • @ramesh.metta,在第一节打印后,我只是退出程序,这将非常快,也将节省您的时间。
  • 真的很好解释 awk ++
  • @RavinderSingh13 我可以在这里更改什么来寻找:location /AuditManagement/ {
  • @ramesh.metta,当然,将/location \/dev/ 更改为/location \/AuditManagement\/ {/,然后它应该会飞起来。
【解决方案2】:

尝试使用基于行的工具解析基于块的文件是一个冒险的提议,没有任何保证。如果在外部 location 块中嵌套了另一个 {...} 块(通过在第一个 } 之后停止而不是匹配的块),大多数解决方案都会失败。这可能不是您的特定文件的问题,但此解决方案可以正确处理它:

awk '(inside && /{/)  { inside++ };
     (inside && /}/)  { --inside; if (!inside) { exit } };
     (inside) {print};
     /location \/dev[ \/{]/ { inside=1; }' src_file

不过,仍有很多方法可以打破这一点。如果这不仅仅是一次性的工作,那么为相关语法设计(或可配置)的实际解析器库将为您提供更可靠的结果。

解释:这个程序的设计使用了一个名为inside 的状态变量,如果它非零,则表示当前正在处理的行在节内。每个新的左大括号 { 都会导致值增加,而每个右大括号 } 都会导致值减小。因此,一旦它在非零之后达到零,我们就知道我们已经完成了目标节并且可以退出。

这些节是按顺序处理的,因此顺序非常重要。最后一节查找开始的location 行并将我们的状态变量设置为1;如果该节是第一个,那么开始的location 行本身将由随后的节打印。但事实上,打印直到文件的下一行才会生效。

所以:如果我们在节内并看到另一个{,请增加inside。 如果我们在节内并看到},则减少inside。如果它现在为零,那意味着我们已经完成了,所以退出。 如果我们在里面(无论我们是否只是看到了 { 或 },只要我们没有退出),然后打印当前行。 如果我们看到 location /dev ... 后跟空格、斜杠或花括号,那么以 /dev 开头的不同位置将不匹配 ... 将 inside 设置为 1 表示我们应该从下一行开始打印。

请注意,正如所写,程序假定整个节的开头{location 指令本身在同一行。如果大括号位于下一行,则嵌套逻辑将意味着它将从该点打印整个文件。这只是这种方法脆弱性的一个例子。

【讨论】:

  • 我喜欢这个,因为我有更复杂的带有许多大括号的子部分。这是有效的,但如果我还有一个带有 /dev2 的节,它也会显示另一部分。
  • 更新更具体。
  • 嗨。我正在尝试调整上面的 awk 语句以查找以下内容: location /AuditManagement/ { 。如果有 / 字符,上述内容会窒息吗?我没有看到任何结果。没有错误
  • / 将终止正则表达式匹配,如果您没有像示例中那样在其前面放置 \。 [...] 之间的字符是选项,因此上面的代码匹配“location /dev”与 /dev 后面的空格,“location /dev/”与斜杠,或“location /dev{”与花括号。因此,如果您将 dev 替换为 AuditManagement 并将其余部分保持不变,则应满足您的要求。
  • 我是这么认为的,正如你为 [..] 解释的那样。但它不工作,或者可能是我做错了。我有两个 /Audit 和 /AuditManagement/。脚本能够找到 /Audit 但不能找到 /AuditManagement/
【解决方案3】:

我不确定你是否可以单独使用 grep 来完成你想要的,但你可以同时使用 sedgrep 命令来获得你想要的输出。

sed -n '/dev {/,/}/p' src_file

会给你以下输出:

location /dev {
  proxy_set_header X-Forwarded-Host $host;
  proxy_set_header X-Forwarded-Server $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_read_timeout 5m;
  proxy_send_timeout 5m;
  proxy_pass http://xyz.9202.com;

           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
  }

如果你想删除 dev {} 行,你可以像这样将 sed 命令与 grep -v 结合起来:

sed -n '/dev {/,/}/p' src_file | grep -v "}" | grep -v "{"

这将为您提供所需的输出:

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass http://xyz.9202.com;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";

【讨论】:

  • FWIW,您可以将两个greps 合并为一个grep -v '[{}]'
  • @RavinderSingh13:我用我尝试过的东西编辑了我的问题。我从早上开始尝试了各种各样的东西。因为它是一个衬里,我只是没有将它添加到我的问题中。我仍然需要解决一个主要逻辑,我问的是一个衬里模式的帮助。也会有非 Unix 人员很快就来查看对这个伟大门户的帮助。
  • @ramesh.metta,感谢您的努力,无论对错,我们只鼓励人们添加他们的努力,我现在也将添加我的版本:) 干杯。
【解决方案4】:

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

sed -n '/^\s*location .*{$/h;/^\s*$\|[{}]/b;G;/location \/dev/P' file

复制以location 开头并以{ 结尾的每一行。将当前副本附加到每一行,如果双行包含location /dev,则仅打印其中的第一个。

注意空行和包含{}的行可以预先删除。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多