在配置文件中将两行合并为一行

Merge two lines into one within a configuration file

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

foo = A_1,GROUP_1,USER_1,USER_2,USER_3

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

foo = A_1,USER_1,SYS_1,SYS_2

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

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

显然我想要一个脚本来完成我的工作。我有标准 shkshawksed, grep, 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 这是某种类型的 [=54=我可以将基于 ] 的代码片段添加到我的更改脚本中。我对其他选择持开放态度,尤其是如果解决方案更简单的话。

未测试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

此 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

有些脏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

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

awk '
/foo = / { old = ","; 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