【问题标题】:How to display content of a file with taking account of other file?如何在考虑其他文件的情况下显示文件的内容?
【发布时间】:2013-07-22 09:46:10
【问题描述】:

我有以下内容的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 的内容。如果 file1file2 中都存在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

编辑

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

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

命令应该尽可能简单

该命令可以包含grepsedawk

【问题讨论】:

  • "if line exists" 应该解释得更清楚一点,这不是“同一行”是否存在,而只是具有相同“名称”的一行:)
  • @SvendHansen 是的,完全正确
  • 我认为我的解决方案可能现在可以工作了,如果输出的顺序不重要:)

标签: linux bash shell sed awk


【解决方案1】:

这是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"}

【讨论】:

  • 我想要一些简单的东西。我更新了我的问题以解释更多。
  • 我的 linux 中没有加入
【解决方案3】:

输入看起来像 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 中的旧值。

【讨论】:

  • 格式化很容易修复,只需将to_json 替换为您的自定义格式。一个更严重的问题可能是它不能保持秩序......但 OP 没有具体说明他想要什么。让我们等一下。
  • 我想要一些简单的东西。我更新了我的问题以解释更多。
  • @MOHAMED:我添加了一个“简单”的 sed 解决方案。
  • sed 命令返回错误:sed: can't open '-': No such file or directory
  • @MOHAMED:然后将第一个sed 命令的输出重定向到带有&gt; temp_file 的临时文件,然后在第二个sed 中使用-f temp_file
【解决方案4】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-06
    • 2012-06-05
    • 2011-06-27
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多