我有两个文件,file1.txt
和file2.txt
,file1.txt
是:
perimeter a=10
perimeter b=15
perimeter c=20
file2.txt
是:
perimeter a=12
perimeter b=14
让我知道sed
或 Perl 覆盖的值file1.txt
输出file1.txt
应该是这样的:
perimeter a=12
perimeter b=14
perimeter c=20
我使用了一系列命令,例如
sed -i -e '/parameter//d r file1.txt' file2.txt
awk -F, 'NR==FNR{a[$1]=$0;next;}a[$1]{$0=a[$1]}1' file1.txt file2.txt
awk -F, 'NR==FNR{a[$1]=$0;next;}a[$1]{$0=a[$1]}1' file2.txt file1.txt
这些都无济于事。
答案1
也许像这样?将其保存为 egscript.pl
并运行 via perl script.pl file1.txt file2.txt
,它会产生您想要的输出。我对文件格式做了一些假设,例如没有需要保留的空行或注释。
#!/usr/bin/env perl
use warnings;
use strict;
use Tie::IxHash;
tie my %hash, 'Tie::IxHash';
while (<>) {
chomp;
my ($k,$v) = split /=/, $_, 2;
$hash{$k} = $v;
}
for my $k (keys %hash) {
print "$k=$hash{$k}\n";
}
更新:不依赖以下版本的Tie::IxHash
:
use warnings;
use strict;
my (%hash, @keys);
while (<>) {
chomp;
my ($key,$value) = split /=/, $_, 2;
push @keys, $key unless exists $hash{$key};
$hash{$key} = $value;
}
for my $key (@keys) {
print "$key=$hash{$key}\n";
}
这也可以浓缩成一句话:
perl -F= -anle 'exists$h{$F[0]}||push@k,$F[0];$h{$F[0]}=$F[1];END{print"$_=$h{$_}"for@k}' file1.txt file2.txt
然而请注意,与脚本不同,如果有多行包含 ,此单行代码将无法正常工作=
。
答案2
您必须对如何实际重写文件保持谨慎。不要这样做:
some_program < file1 > file1
因为 shell 负责处理重定向前启动该程序时,>
重定向将截短文件前程序就有机会读取它。
以下是一些替代方案:
明确写入临时文件,然后覆盖原始文件
tmpfile=$(mktemp) some_program < file1 > "$tmpfile" && mv "$tmpfile" file1
使用
&&
,所以你只mv
如果程序成功退出。使用
sponge
命令moreutils
包裹some_program < file1 | sponge file1
如果您使用 GNU awk,您可以使用扩展
-i inplace
,但您需要稍微调整一下程序:gawk -i inplace -F= ' NR == FNR {a[$1] = $0; print; next} $1 in a {$0 = a[$1]} 1 ' file{2,1}.txt
选项 2 和 3 也使用临时文件,但会为您处理细节。