Search code examples
jsontemplatesjqinode

jq "Invalid numeric literal"


I'm trying to parse the output of a df -i command into a json template using jq, but I am receiving the following error when reading in numeric values from an array into the json template:

parse error: Invalid numeric literal at line 10, column 31

Here is the script:

#!/bin/sh

JSON=`cat inode-template.json`

#get number of rows in output
numLines=`df -ih | wc -l`

#row iterator (start at 2 to skip the header row)
for ((row=2; row<=$numLines; row++))
do
  arrCounter=0 #array counter reset
  #for each value in col (minus 'mounted on'):
  for ((val=1; val<=5; val++))
  do
    value=$(df -iP | awk -v value=$val 'NR=='$row'{sub( "%", "", $value); print $value }') #get each value
    tempArr[$arrCounter]=$value #store row value
    arrCounter=$((arrCounter + 1))
  done
  echo "${tempArr[@]}"
  #Assign values in json template
  JSON=$(jq -c --arg filesys "${tempArr[0]}" --arg iTotal ${tempArr[1]} --arg iUsed ${tempArr[2]} --arg iFree ${tempArr[3]} --arg iPercent ${tempArr[4]} '.metrics[].fileSystem |= $filesys | .metrics[].InodesTotal |= $iTotal | .metrics[].InodesUsed |= $iUsed | .metrics[].InodesFree |= $iFree | .metrics[].InodeUsedPercent |= $iPercent' inode-template.json)

  echo "${JSON}"
  tempArr=() #reset array
done

And here is the json template being read in:

{
  "name": "inodeparse",
  "metrics": [
    {
      "event_type": "test",
      "provider": "test",
      "fileSystem": "FILESYS",
      "InodesTotal":NODESTOTAL,
      "InodesUsed":NODESUSED,
      "InodesFree":NODESFREE,
      "InodeUsedPercent":NODESPERCENT
    }
  ]
}

Is there any option to declare an array item as an integer before using jq to replace the value in the template?


Solution

  • The short answer to your question is: (1) you will have to adopt a slightly different approach as the "template" file is neither valid JSON nor valid as a jq program (2) use --argjson instead of --arg, at least for the numbers (if your jq does not support --argjson, it's very old -- definitely time to upgrade :0)

    The jq Cookbook has a section describing two simple approaches to templating with jq. You'd be much better off using one of those approaches if at all possible.

    The following illustrates the "$-variables" approach using your template, modified so that the template variables are jq "$-variables":

    $ cat inode-template.jq
    {
      "name": "inodeparse",
      "metrics": [
        {
          "event_type": "test",
          "provider": "test",
          "fileSystem": "FILESYS",
          "InodesTotal": $NODESTOTAL,
          "InodesUsed": $NODESUSED,
          "InodesFree": $NODESFREE,
          "InodeUsedPercent": $NODESPERCENT
        }
      ]
    }
    

    Now we can instantiate the template using the --argjson command-line option of jq:

    $ jq -n --argjson NODESTOTAL 1 --argjson NODESUSED 2 --argjson NODESFREE 3 --argjson NODESPERCENT 4 -f inode-template.jq
    {
      "name": "inodeparse",
      "metrics": [
        {
          "event_type": "test",
          "provider": "test",
          "fileSystem": "FILESYS",
          "InodesTotal": 1,
          "InodesUsed": 2,
          "InodesFree": 3,
          "InodeUsedPercent": 4
        }
      ]
    }
    

    The other approach is perfectly fine too.

    Of course, you could also use the field-assignment approach. Indeed, all three approaches are mutually compatible.