【问题标题】:Append a string after a multiple line match in bash在bash中的多行匹配后附加一个字符串
【发布时间】:2017-09-14 05:14:51
【问题描述】:

我需要 sedawk 命令在 bash 中的多行匹配后打印一行。例如将此文件作为输入:

admin:
  users:
    - user1
    - user2

read_only:
  users:
    - user3

write_only:
  users:
    - user4

access_only:
  users:
    - user5
    - user6

..我需要将其作为输出:

admin:
  users:
    NEW USER
    - user1
    - user2

read_only:
  users:
    - user3

write_only:
  users:
    - user4

access_only:
  users:
    - user5
    - user6

正则表达式应匹配前两行admin: users:,然后附加NEW USER 行。

如何做到这一点?我应该使用 sed 还是 awk?如何? 谢谢

【问题讨论】:

  • 请提供更多信息,包括示例输入和预期输出,以便我们清楚地为您提供帮助。
  • 如果您正在查看sed/awk,那么您为什么还要将其标记为linuxperlbash
  • @RavinderSingh13 更新更清晰! :)
  • @Sobrique 因为我知道这也可以用 awk 和 perl 正则表达式来完成。
  • 这是问题的错误解决方案——它是上下文语法,而正则表达式不这样做。

标签: bash awk sed


【解决方案1】:

sed 用于单个行的简单替换,就是这样。对于其他任何事情,您都应该使用 awk 来实现简单、清晰、高效、稳健和大多数其他理想的软件属性。

使用 GNU awk 进行多字符 RS 和 gensub():

$ awk -v RS='^$' '{$0=gensub(/(admin:\s+users:)(\s+)/,"\\1\\2NEW LINE\\2",1)}1' file
admin:
  users:
    NEW LINE
    - user1
    - user2

【讨论】:

  • 我发布了整个 yaml 文件,但您的 awk 似乎无法正常工作。我哪里错了?
  • 我最好的猜测是您没有使用 gawk,因为我的回答状态是必需的。如果你使用 gawk,它会工作,如果不是,那么它不会。
【解决方案2】:

两者都没有。它是 YAML,所以将其解析为 YAML。

#!/usr/bin/env perl

use strict;
use warnings;

use YAML;
use Data::Dumper;

my $stuff = Load ( do { local $/; <DATA> } );

print Dumper $stuff; 

print $stuff->{admin}{users}[0],"\n";

print "List:\n";
print join "\n", @{$stuff->{admin}{users}};

__DATA__
admin:
  users:
    - user1
    - user2

鉴于您已将问题更新为“将新用户添加到管理员用户节”,我的答案已更改 - 但仍然是“使用 perl”:

#!/usr/bin/env perl

use strict;
use warnings;

use YAML::Syck;
use Data::Dumper;

$YAML::Syck::SortKeys = 1;

my $stuff = Load ( do { local $/; <DATA> } );

print Dumper $stuff; 

push @{$stuff->{admin}{users}}, "new username";

print Dumper $stuff;

print Dump \$stuff;


__DATA__
admin:
  users:
    - user1
    - user2

read_only:
  users:
    - user3

write_only:
  users:
    - user4

access_only:
  users:
    - user5
    - user6

您可以将其单线化为:

perl -0777 -MYAML -e '$s = Load(<>);unshift @{$s->{admin}{users}}, "new username"; print Dump $s;'

这将从 STDIN 读取,进行转换并输出有效的 YAML。 unshift 将在数组的开头插入用户名 - 我通常使用 push 在末尾插入它。

注意 - 我在前面的示例中使用了YAML::Syck,因为它会对输出进行排序。

【讨论】:

  • 是的,我知道它是 yaml,但我需要在 bash 脚本中解析该文件。
  • 你标记了你的问题perl
【解决方案3】:

sed

/^[a-z_]\+://^$/ 之间划分块,这样你就可以在一个块中完成一些过程:

sed -e $'/^admin:/,/^$/{/users:/a\    NewUser\n}'

可以这样写:

NEWUSER="user2017"
sed -e "/^admin:/,/^$/{
    /users:/a\    - $NEWUSER
}"

(通过使用双引号,我可以使用命令行或脚本中的变量)

这将呈现:

admin:
  users:
    - user2017
    - user1
    - user2

read_only:
  users:
    - user3

...

...

myadduser () { 
    local crt= line;
    while IFS= read line; do
        echo "$line";
        [ "$line" ] && { 
            [ "${line:0:1}" != " " ] && crt=${line%:} || { 
                [ "${line//*([: ])}" = "users" ] &&
                    [ "$crt" = "${2:-admin}" ] &&
                        printf "    - %s\n" $1
            }
        };
    done
}

可以使用:

myadduser NewAdminUser <file.txt
admin:
  users:
    - NewAdminUser
    - user1
    - user2

read_only:
  users:
    - user3

...

或带有可选的第二个参数:

myadduser NewWriteUser write_only
admin:
  users:
    - user1
    - user2

read_only:
  users:
    - user3

write_only:
  users:
    - NewWriteUser
    - user4

access_only:
  users:
    - user5
    - user6

但是请,

阅读Sobrique's fine answer

使用 进行原型设计,例如使用 进行最终编程,您可能会找到合适的库。

也可以在 中使用任何此类库。

第一个管理员用户
perl -MYAML -E 'undef$/;$s = Load(<>);say@{$s->{admin}->{users}}[0];'
管理员用户数
perl -MYAML -E 'undef$/;$s = Load(<>);say scalar@{$s->{admin}->{users}};' 

【讨论】:

    【解决方案4】:

    如果您在 Unix(或不使用回车符的其他环境)中工作,您可以通过临时替换新行来使用 sed:

    cat file | tr '\n' '\r' | sed "s/admin:\r  users:/admin:\r  users:\r    NEW LINE/g" | tr '\r' '\n'
    

    诚然,它比上面的 awk 解决方案更 hack,但为那些想知道如何使用 sed 轻松完成它的人添加这个。

    【讨论】:

    • 谢谢!这运作良好!现在我只需要了解你的命令的语法 lol
    【解决方案5】:

    awk解决方案:

    awk '/^admin/{ n=NR+2 }NR==n{ sp=substr($0,1,index($0,"-")-1); 
         printf "%s\n%s\n",sp"NEW USER",$0; next  }1' file
    

    输出:

    admin:
      users:
        NEW USER
        - user1
        - user2
    
    read_only:
      users:
        - user3
    
    write_only:
      users:
        - user4
    
    access_only:
      users:
        - user5
        - user6
    

    【讨论】:

    • 感谢您的解决方案!正确
    【解决方案6】:

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

    sed -i '/admin:/!b;n;/users:/!b;n;h;s/-.*/- new user/p;g' file
    

    如果该行不包含admin:,则退出。同样,如果以下行不包含users: bail out。阅读并复制以下行,修改该行以包含- new user,然后打印它和原件。

    【讨论】:

      猜你喜欢
      • 2011-09-25
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-09
      • 2015-02-26
      • 2022-01-03
      相关资源
      最近更新 更多