0

我有包含以下内容的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 命令来显示file1file2的内容。如果 aname同时存在于file1file2中,那么我只想显示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

编辑

file1file2具有完全相同的内容格式:

{"name":"any", "value":"xx"}

命令应该尽可能简单

该命令可以包含grep, sed,awk

4

4 回答 4

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"}
于 2013-07-22T13:13:58.723 回答
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"}
于 2013-07-22T10:09:41.370 回答
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中的旧值。

于 2013-07-22T10:07:04.267 回答
1

对我的解决方案进行了简单的更改,它似乎有效:

输出的顺序重要吗?否则你可以这样做,使用sort

cat file2 file1 | sort -u -s --key=1

变化是增加--key=1。这意味着它将对每行的第一列(直到第一个空格)进行排序。-s当另一种排序找到两个相等时,使其不在该行的其余部分使用排序。文件的顺序决定了匹配时将使用哪个文件的行。

这将输出按字母顺序排序的结果。看起来输入已经是,在这种情况下,它应该完全符合您的描述(我相信)。否则,它将更改行的顺序(要排序)。不确定这是否有问题?

于 2013-07-22T10:57:52.593 回答