jq使用外部文件作为--arg参数值

2021-10-24  本文已影响0人  CodingCode
  1. 文件包含文本(字符串数据)
  1. 方法 1: --arg DATA "$(<data.json)"
$ jq --arg DATA "$(<data.txt)" ...

举例来说:

$ cat data.txt
abcd
$ echo '{"payload":{"data":{"update":null}}}' | jq --arg DATA "$(<data.txt)" '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "abcd"
    }
  }
}

注意"$(<data.txt)"要加双引号,因为这个实际是shell的语法,不是jq的语法:

  1. shell读取文件data.txt的内容,拼成字符串再传给jq,所以jq拿到的就是字符串了。
  2. 如果是单引号,则shell不会去解析文件读取操作,会把命令本身($(<data.txt>)作为字符串传给jq。
$ echo '{"payload":{"data":{"update":null}}}' | jq --arg DATA '$(<data.txt)' '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "$(<data.txt)"
    }
  }
}
  1. 如果没有引号,那么当data.txt内容含有特殊符号,例如空格,换行符之类时,整个jq命令解释就会失败(这实际上还是shell无法解析命令行)。
$ cat data.txt
line1-1 line1-2
line2

$ # without double-quote
$ echo '{"payload":{"data":{"update":null}}}' | jq --arg DATA $(<data.txt) '.payload.data.update = $DATA'
jq: error: line1/0 is not defined at <top-level>, line 1:
line1-2
jq: 1 compile error

$ # with double-quote
$ echo '{"payload":{"data":{"update":null}}}' | jq --arg DATA "$(<data.txt)" '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "line1-1 line1-2\nline2"
    }
  }
}
  1. 方法2:使用参数--rawfile

还用前面的例子:

$ echo '{"payload":{"data":{"update":null}}}' | jq --rawfile DATA data.txt '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "abcd\n"
    }
  }
}

看到这和方法1有区别,它把文件的最后一个换行符读出来了,作为文本的内容了。这其实都是shell的功能在起作用。

说道这儿了,咱们就整一个二进制的文件看看两者区别是什么?

$ hexdump -C data.txt 
00000000  7f 45 4c 46 02 01 01 00  00 00                    |.ELF......|
0000000a

$ echo '{"payload":{"data":{"update":null}}}' | jq --rawfile DATA data.txt '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "\u007fELF\u0002\u0001\u0001\u0000\u0000\u0000"
    }
  }
}

$ echo '{"payload":{"data":{"update":null}}}' | jq --arg DATA "$(<data.txt)" '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": "\u007fELF\u0002\u0001\u0001"
    }
  }
}

看到,"$(<data.txt)"格式把文本里面的字符'\0'自动过滤掉了呢。

  1. 文件包含json子串
  1. 方法1:--argjson DATA "$(<data.json)"
$ cat data.json 
{ "name": "Tom", "age": 30}

$ echo '{"payload":{"data":{"update":null}}}' | jq --argjson DATA "$(<data.json)" '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": {
        "name": "Tom",
        "age": 30
      }
    }
  }
}
  1. 方法2:使用参数--slurpfile
$ echo '{"payload":{"data":{"update":null}}}' | jq --slurpfile DATA data.json '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": [
        {
          "name": "Tom",
          "age": 30
        }
      ]
    }
  }
}

两者有啥区别呢,好像没啥区别?
你仔细看了,再仔细看!
两者区别大了。--slurpfile方式update的值变成数组([...])了哦。
jq的文档(https://stedolan.github.io/jq/manual/)说的非常明确:

* --slurpfile variable-name filename:
This option reads all the JSON texts in the named file and
binds an array of the parsed JSON values to the given global variable.
If you run jq with --slurpfile foo bar, then $foo is available in the program and 
has an array whose elements correspond to the texts in the file named bar.

所以在这个场景里,我们需要用下标$DATA[0]来指示:

$ echo '{"payload":{"data":{"update":null}}}' | jq --slurpfile DATA data.json '.payload.data.update = $DATA[0]'
{
  "payload": {
    "data": {
      "update": {
        "name": "Tom",
        "age": 30
      }
    }
  }
}

所以--slurpfile可以支持文件是多个数据的格式:

$ cat data.json 
{ "name": "Tom",   "age": 30}
{ "name": "Jerry", "age": 30}
{ 
  "name": "Mick", 
  "age": 40
}

$ echo '{"payload":{"data":{"update":null}}}' | jq --slurpfile DATA data.json '.payload.data.update = $DATA'
{
  "payload": {
    "data": {
      "update": [
        {
          "name": "Tom",
          "age": 30
        },
        {
          "name": "Jerry",
          "age": 30
        },
        {
          "name": "Mick",
          "age": 40
        }
      ]
    }
  }
}

这里data.json的内容不是一个标准的json数据,不是数组类型,也不是map类型,不伦不类;所以(jq --argjson DATA "$(<data.txt)")格式就无法支持这种。

上一篇 下一篇

猜你喜欢

热点阅读