shell 使用csv文件内容更新json文件

fivyi3re  于 2023-01-21  发布在  Shell
关注(0)|答案(3)|浏览(204)

我有两个文件:修改.csv和sample.json。我的csv文件如下:

header "a", "b"
      "a11","b1"
      "a22","b2"
      "a33","b3"

json文件是这样的:

[
 {"a":"a1","b":"b1"},
 {"a":"a2","b":"b2"},
 {"a":"a3","b":"b3"},
 {"a":"a4","b":"b4"}
]

我需要编写一个jq命令,该命令使用csv文件在json文件中进行更改,即json文件的最终输出应如下所示:

[
 {"a":"a11","b":"b1"},
 {"a":"a22","b":"b2"},
 {"a":"a33","b":"b3"},
 {"a":"a4","b":"b4"}
]

我编写了以下命令:

while IFS=",", read f1 f2
do
  jq --argjson k1 $f1 --argjson k2 $f2 '(.[] | select(.b == $k2) | .a) |= $k1' sample.json| sponge sample.json
done < changes.csv

虽然每次迭代它都能过滤和更新key“a”的值,但是当我尝试将结果海绵到json文件中时,它却无法做到这一点,不知道我到底错过了什么。

j2datikz

j2datikz1#

假设CSV行为良好,您可以编写:

# Skip the CSV header row by NOT specifying the -n option
< changes.csv | jq -Rcr --argfile json sample.json '
  def trim: sub("^[ \t]*\""; "") | sub("\"[ \t]*$";"");
  INDEX(inputs | split(",") | map(trim) | select(length>0); .[1]) as $dict
  | $json
  | map( .a = $dict[.b][0] )
'

对于更复杂的CSV,您可能希望使用CSV到JSON或CSV到TSV工具(这两种工具都可以很容易地用jq编写--参见https://rosettacode.org/wiki/Convert_CSV_records_to_TSV#jq)
如果您不喜欢使用--argfile选项,那么可以使用其他方法阅读这两个文件,例如,您可以使用--rawfile读取CSV,而保留STDIN读取JSON。

2fjabf4q

2fjabf4q2#

对于纯粹的jq解决方案,最好确保CSV在任何字段中都不包含任何,"\n,否则代码将变得复杂得多。
因此,我建议使用Miller(可用于多个操作系统的here)解决方案,它可以轻松、可靠地完成任务:

mlr --icsv --ojson --no-jvstack join --ijson -f file.json -j 'b' --ul file.csv
[
{"b": "b1", "a": "a11"},
{"b": "b2", "a": "a22"},
{"b": "b3", "a": "a33"}
]

让我们分解命令:

mlr join -f file1 -j 'b' file2

将在字段b上连接 * file1 * 和 * file2 。当b以外的字段存在于两个文件中时(例如a),则输出的是 * file2 * 的值。
因此,要使用CSV中的值更新JSON,
file1 * 应为JSON,* file2 * 应为CSV。

  • --ul表示输出 * file1 * 不可连接的行;没有它,输出将仅包含已经"配对"的记录。
  • 使用join动词,Miller允许为 * file1 * 指定与 * file2 * 不同的输入格式;您将默认输入格式设置为--icsv(用于处理 * file2 *),并在join动词(用于处理 * file1 *)之后用--ijson覆盖它。
      • 输出格式**设置为JSON,--ojson表示每行输出一条"记录"。
5t7ly7z5

5t7ly7z53#

所以我发现如果我把csv文件的值转换成输入可读的格式,并使用--arg代替--argjson,文件就能很好地更新。所以,命令awk '{gsub(/"/,"")};1' b.csv > c.csv会把csv文件转换成:

a11,b1
a22,b2
a33,b3

因此,现在如果应用命令:

while IFS=",", read f1 f2
do
  jq --arg k1 $f1 --arg k2 $f2 '(.[] | select(.b == $k2) | .a) |= $k1' sample.json| sponge sample.json
done < c.csv

将发生期望的改变。
它在旧格式中失败的合理原因是,当我们使用“read”命令读取值时,它会将"a"转换为"\"a\""。因此,每次我追加答案时,它都会返回void answer。
答案适用于任何类型的json格式。例如,如果json对象如下:

[
 { 
  "d": { "a":"a1", "c":"c1"},
  "b": "b1"
  }
]

我们可以将命令更新为'(.[] | select(.b == $k2) | .d.a) |= $k1'
我没有其他方法张贴,但我感谢的帮助。谢谢乡亲(@峰和@弗拉瓦多纳)

相关问题