我的 file1 包含以下内容
{"name":"clio5", "value":"13"}
{"name":"citroen_c4", "value":"23"}
{"name":"citroen_c3", "value":"12"}
{"name":"golf4", "value":"16"}
{"name":"golf3", "value":"8"}
我的 file2 包含以下内容
{"name":"clio5", "value":"14"}
{"name":"citroen_c4", "value":"25"}
{"name":"golf4", "value":"18"}
我想执行shell命令以显示 file1 和 file2 的内容。如果 file1 和 file2 中同时存在name
,那么我只想显示 file2 的相关行。
所以输出应该如下所示:
$command taking account file1 file2
{"name":"clio5", "value":"14"}
{"name":"citroen_c4", "value":"25"}
{"name":"citroen_c3", "value":"12"}
{"name":"golf4", "value":"18"}
{"name":"golf3", "value":"8"}
该命令不应编辑 file1 file2
修改
file1 和 file2 具有相同的内容格式:
{"name":"any", "value":"xx"}
命令应该尽可能简单
该命令可能包含grep
,sed
,awk
答案 0 :(得分:3)
以下是awk
的一种方式:
awk -F"[:,]" '
NR==FNR { name[$2]=$0;next }
($2 in name) { delete name[$2]; print $0 }
END { for (left in name) print name[left] }' file1 file2
$ head file*
==> file1 <==
{"name":"clio5", "value":"13"}
{"name":"citroen_c4", "value":"23"}
{"name":"citroen_c3", "value":"12"}
{"name":"golf4", "value":"16"}
{"name":"golf3", "value":"8"}
==> file2 <==
{"name":"clio5", "value":"14"}
{"name":"citroen_c4", "value":"25"}
{"name":"golf4", "value":"18"}
$ awk -F"[:,]" '
NR==FNR { name[$2]=$0;next }
($2 in name) { delete name[$2]; print $0 }
END { for (left in name) print name[left] }' file1 file2
{"name":"clio5", "value":"14"}
{"name":"citroen_c4", "value":"25"}
{"name":"golf4", "value":"18"}
{"name":"golf3", "value":"8"}
{"name":"citroen_c3", "value":"12"}
答案 1 :(得分:2)
一种方法是使用join
加入名称字段上的两个文件,然后使用awk
更改值。如下所示:
$ join -t, -a1 <(sort file1) <(sort file2) | awk -F, -vOFS=, '{if($3){$2=$3;NF-=1}}1' | sed 's/new_value/value/g'
{"name":"citroen_c3", "value":"12"}
{"name":"citroen_c4", "value":"25"}
{"name":"clio5", "value":"14"}
{"name":"golf3", "value":"8"}
{"name":"golf4", "value":"18"}
join
要求两个文件都在连接键上排序。
或者,如果订购对您很重要,您可以使用循环读取每一行,然后使用第二个文件grep获取新值。如下所示:
while IFS= read -r line
do
if [[ $line =~ name\":\"([^\"]*)\" ]]
then
name=${BASH_REMATCH[1]}
newVal=$(grep "\"name\":\"$name\"" file2 | sed 's/^.*"\([^"]\+\)"}$/\1/g')
if [[ -z $newVal ]]
then
echo "$line"
else
echo "{\"name\":\"$name\", \"value\":\"$newVal\"}"
fi
fi
done < file1
输出:
{"name":"clio5", "value":"14"}
{"name":"citroen_c4", "value":"25"}
{"name":"citroen_c3", "value":"12"}
{"name":"golf4", "value":"18"}
{"name":"golf3", "value":"8"}
答案 2 :(得分:1)
输入看起来像JSON。使用适当的工具,Perl的JSON库:
#!/usr/bin/perl
use warnings;
use strict;
use JSON qw(from_json to_json);
my %hash;
for my $file (qw/file1 file2/) {
open my $FH, '<', $file or die $!;
while (<$FH>) {
my $j = from_json($_);
$hash{$j->{name}} = $j->{value} // $j->{new_value};
}
}
while (my ($name, $value) = each %hash) {
print to_json({name => $name, value => $value}), "\n";
}
它读取两个文件,在读取第二个文件时覆盖这些值。对我来说,输出并不完全符合您的预期:
{"value":"14","name":"clio5"}
{"value":"18","name":"golf4"}
{"value":"25","name":"citroen_c4"}
{"value":"8","name":"golf3"}
{"value":"12","name":"citroen_c3"}
正如JSON所说,它等同于您的预期输出,因此如果您始终使用适当的库,您将不会注意到差异。如果没有,你必须再调整代码。
或者,如果你真的想使用sed:
sed 's%\({"name":"[^"]*", \)"new_value":\("[^"]*"\)}%s/\1"value.*/\1"value":\2}/%' file2 \
| sed -f- file1
第一个sed调用将 file2 转换为sed脚本,该脚本替换 file1 中的旧值。
答案 3 :(得分:1)
对我的解决方案进行简单的更改,它似乎有效:
输出的顺序是否重要?否则,您可以使用sort
:
cat file2 file1 | sort -u -s --key=1
更改正在添加--key=1
。这意味着它将对每行的第一列(直到第一个空白)进行排序。 -s
使得它不会在行的其余部分使用排序,而另一个排序找到两个等号。文件的顺序决定了匹配时将使用哪个文件的行。
这将按字母顺序输出结果。看起来输入已经是,在这种情况下,它应该完全符合您的描述(我相信)。否则,将更改行的顺序(待排序)。不确定这是不是一个问题?