【问题标题】:Merge two lines into one within a configuration file在配置文件中将两行合并为一行
【发布时间】:2015-06-12 05:55:41
【问题描述】:

我有几个带有配置文件的 AIX 系统,我们称之为 /etc/bar/config。该文件可能有也可能没有声明 foo 值的行。一个例子是:

foo = A_1,GROUP_1,USER_1,USER_2,USER_3

foo 行在所有系统上可能相同,也可能不同。不同的系统可能具有不同的值和不同的不同数量的值。我的任务是在所有系统的 config 文件中添加“最低限度”值。最低限度的线将如下所示。

foo = A_1,USER_1,SYS_1,SYS_2

如果该行不存在,我必须创建它。如果这条线确实存在,我必须合并两条线。使用我的示例,结果将是这样的。值的顺序无关紧要。

foo = A_1,GROUP_1,USER_1,USER_3,USER_2,SYS_1,SYS_2

显然我想要一个脚本来完成我的工作。我有标准的shkshawksedgrep、perl、cut 等。由于这是 AIX,我无法访问这些实用程序的 GNU 版本。

最初,我有一个包含这些命令的脚本来替换整个 foo 行。

cp /etc/bar/config /etc/bar/config.$$
sed "s/foo = .*/foo = A_1,USER_1,SYS_1,SYS_2/" /etc/bar/config.$$ > /etc/bar/config

但这只是替换了该行。它确实考虑了任何预先存在的配置,包括缺少的行。而且我正在脚本中进行其他配置修改,例如向其他文件添加完全唯一的行并重新启动进程,所以我perfer这是某种基于shell的代码sn-p我可以添加到我的更改脚本中。我对其他选项持开放态度,尤其是在解决方案更简单的情况下。

【问题讨论】:

    标签: perl shell awk sed ksh


    【解决方案1】:

    一些肮脏的 bash/sed:

    #!/usr/bin/bash
    input_file="some_filename"
    v=$(grep -n '^foo *=' "$input_file")
    lineno=$(cut -d: -f1 <<< "${v}0:")
    base="A_1,USER_1,SYS_1,SYS_2,"
    if [[ "$lineno" == 0 ]]; then
        echo "foo = A_1,USER_1,SYS_1,SYS_2" >> "$input_file"
    else 
        all=$(sed -n ${lineno}'s/^foo *= */'"$base"'/p' "$input_file" | \
            tr ',' '\n' | sort | uniq | tr '\n' ',' | \
            sed -e 's/^/foo = /' -e 's/, *$//' -e 's/   */ /g' <<< "$all") 
        sed -i "${lineno}"'s/.*/'"$all"'/' "$input_file"
    fi
    

    【讨论】:

    • 我可能有 BASH 可用。不过,看起来这将适用于 KORN。如果我可以投票给你lineno=$(cut -d: -f1 &lt;&lt;&lt; "${v}0:"),我会的。我不知道你可以用这些来做到这一点。
    • 另外,整个使用 tr 对字符串进行排序/uniq 非常酷
    【解决方案2】:

    未经测试的 bash 等

    config=/etc/bar/config
    default=A_1,USER_1,SYS_1,SYS_2
    pattern='^foo[[:blank:]]*=[[:blank:]]*'   # shared with grep and sed
    if current=$( grep "$pattern" "$config" | sed "s/$pattern//" )
    then 
        new=$( echo "$current,$default" | tr ',' '\n' | sort | uniq | paste -sd, )
        sed "s/$pattern.*/foo = $new/" "$config" > "$config.$$.tmp" &&
            mv "$config.$$.tmp" "$config"
    else
        echo "foo = $default" >> "$config"
    fi
    

    普通的 perl 解决方案:

    perl -i -lpe '
        BEGIN {%foo = map {$_ => 1} qw/A_1 USER_1 SYS_1 SYS_2/}
        if (s/^foo\s*=\s*//) {
            $found=1; 
            $foo{$_}=1 for split /,/; 
            $_ = "foo = " . join(",", keys %foo);
        }
        END {print "foo = " . join(",", keys %foo) unless $found}
    ' /etc/bar/config
    

    【讨论】:

    • OP 说“因为这是 AIX,所以我无法访问这些实用程序的 GNU 版本。”
    • 我有时可以解决诸如“sed -i”之类的 GNU 问题。 man(1) 仍然是我的朋友,知道什么有效,什么无效。
    • 我想我已经从第一个中删除了 GNUiness。仍然很大程度上未经测试
    • 这两个例子都依赖于 = 的两侧恰好有一个空格
    • 我的原始配置文件在= 的两侧确实有一个空格。我想保留它。
    【解决方案3】:

    这个 Perl 代码会按照你的要求去做。它希望将文件的路径作为命令行上的参数进行修改。

    请注意,它将整个输入文件读入数组@config,然后用修改后的数据覆盖同一个文件。

    它的工作原理是根据foo = 行中已存在的项目和@defaults 中的默认项目列表的组合构建散列%values。该组合按字母顺序排序并用逗号连接

    use strict;
    use warnings;
    
    my @defaults = qw/ A_1 USER_1 SYS_1 SYS_2 /;
    
    my ($file) = @ARGV;
    my @config = <>;
    
    open my $out_fh, '>', $file or die $!;
    select $out_fh;
    
    for ( @config ) {
      if ( my ($pfx, $vals) = /^(foo \s* = \s* ) (.+) /x ) {
        my %values;
        ++$values{$_} for $vals =~ /[^,\s]+/g;
        ++$values{$_} for @defaults;
        print $pfx, join(',', sort keys %values), "\n";
      }
      else {
        print;
      }
    }
    
    close $out_fh;
    

    输出

    foo = A_1,GROUP_1,SYS_1,SYS_2,USER_1,USER_2,USER_3
    

    【讨论】:

    • 如果在文件中找不到foo行,则需要添加。
    • 或者至少确定foo 是否不存在。我可能必须问题中未提及的其他内容才能将其插入正确的位置。
    【解决方案4】:

    由于您没有提供示例输入和预期输出,我无法对此进行测试,但这是正确的方法:

    awk '
    /foo = / { old = ","$3; next }
    { print }
    END {
        split("A_1,USER_1,SYS_1,SYS_2"old,all,/,/)
        for (i in all)
            if (!seen[all[i]]++)
                new = (new ? new "," : "") all[i]
        print "foo =", new
    }
    ' /etc/bar/config > tmp && mv tmp /etc/bar/config
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多