1

I have a JSON array of objects like so .

[
  {
    "id" : "tmp1387816934708382026",
    "owner" : "john",
    "x11-display" : ":5",
    "x11-authority" : "/run/user/john/dcv/tmp1387816934708382026.xauth",
    "num-of-connections" : 1,
    "creation-time" : "2019-05-14T14:12:14.989287Z",
    "last-disconnection-time" : "2019-05-31T18:58:42.851223Z"
  },
  {
    "id" : "tmp4241942441012516520",
    "owner" : "mike",
    "x11-display" : ":10",
    "x11-authority" : "/run/user/mike/dcv/tmp4241942441012516520.xauth",
    "num-of-connections" : 0,
    "creation-time" : "2019-05-17T16:23:05.891531Z",
    "last-disconnection-time" : "2019-05-19T11:23:30.844797Z"
  }
]

I need to add a key named days-idle with a value to each object that is calculated inside my bash script. This is what I'm looking for in each JSON object.

{
    "id" : "tmp1387816934708382026",
    "owner" : "mike",
    "x11-display" : ":5",
    "x11-authority" : "/run/user/mike/dcv/tmp1387816934708382026.xauth",
    "num-of-connections" : 1,
    "creation-time" : "2019-05-14T14:12:14.989287Z",
    "last-disconnection-time" : "2019-05-31T18:58:42.851223Z",
    "days-idle" : "$daysIdle"
  }

I know you can add a key with jq but wasn't sure of how to go about adding a key, value pair where the value is a bash variable.

2
  • please check if this stackoverflow.com/questions/48512598/… will help you.
    – asktyagi
    Commented Jun 7, 2019 at 13:56
  • 1
    Do you want the value of the $daysIdle expanded right? Then why do you show it $daysIdle without expanded in the desired output?
    – Inian
    Commented Jun 10, 2019 at 10:48

2 Answers 2

2

Assuming you want to add your new key to the element with a particular .id value, $id:

jq --arg id "$id" --arg idle "$daysIdle" \
    '( .[] | select(.id == $id)."days-idle" ) |= $idle' file

This picks out the array element with the .id that we want to modify, then adds (actually updates) the ."days-idle" key to that element with the particular value that we want it to have.

If ."days-idle" should be the time between the ."last-disconnection-time" timestamp and now, then you can update all the elements in the JSON like so:

jq 'def dayssince: ((now - (sub("[.].*"; "Z") | fromdate))/86400) | round;
    map(. += { "days-idle": (."last-disconnection-time" | dayssince) })' file

The sub() call will truncate the timestamp at the dot and replace the end of it with Z. This is due to fromdate being a bit restrictive in the type of timestamps that it can parse and doesn't handle the sub-second precision of the original timestamp string.

I decided to put the actual calculation of the number of days since the timestamp as a jq function called dayssince, just to keep the code tidy.

The resulting JSON (when running on 28 Jun, 2021):

[
  {
    "id": "tmp1387816934708382026",
    "owner": "john",
    "x11-display": ":5",
    "x11-authority": "/run/user/john/dcv/tmp1387816934708382026.xauth",
    "num-of-connections": 1,
    "creation-time": "2019-05-14T14:12:14.989287Z",
    "last-disconnection-time": "2019-05-31T18:58:42.851223Z",
    "days-idle": 759
  },
  {
    "id": "tmp4241942441012516520",
    "owner": "mike",
    "x11-display": ":10",
    "x11-authority": "/run/user/mike/dcv/tmp4241942441012516520.xauth",
    "num-of-connections": 0,
    "creation-time": "2019-05-17T16:23:05.891531Z",
    "last-disconnection-time": "2019-05-19T11:23:30.844797Z",
    "days-idle": 771
  }
]
0

First, I am going to assume that you have the JSON object array in a Bash variable so let's start with that:

bash$ object='[
  {
    "id" : "tmp1387816934708382026",
    "owner" : "john",
    "x11-display" : ":5",
    "x11-authority" : "/run/user/john/dcv/tmp1387816934708382026.xauth",
    "num-of-connections" : 1,
    "creation-time" : "2019-05-14T14:12:14.989287Z",
    "last-disconnection-time" : "2019-05-31T18:58:42.851223Z"
  },
  {
    "id" : "tmp4241942441012516520",
    "owner" : "mike",
    "x11-display" : ":10",
    "x11-authority" : "/run/user/mike/dcv/tmp4241942441012516520.xauth",
    "num-of-connections" : 0,
    "creation-time" : "2019-05-17T16:23:05.891531Z",
    "last-disconnection-time" : "2019-05-19T11:23:30.844797Z"
  }
]'

Next, I'll assume that $daysIdle is also variable and contains a number:

bash$ daysIdle=3

Now, we can echo the $object through jq to add in that variable.

bash$ echo "$object" | jq --arg daysIdle "$daysIdle" '.[]."days-idle" = ($daysIdle | tonumber)'

Some important notes about that. If the objects are actually in a file or coming from some other stream like cURL, just replace echo $object which whatever is appropriate. Second, I am assuming that you want it as a JSON number and not a string which --arg normally creates so I have a filter in it to fix that. Lastly, note that I use the --arg option to jq to pass in the value. This is far better and safer than trying to embed the value into the JSON filter string itself and won't cause a syntax error in it. It will throw an error if it can't cast to a number, but it won't allow arbitrary injection into the filter string. With that said, let's see the output:

[
  {
    "id": "tmp1387816934708382026",
    "owner": "john",
    "x11-display": ":5",
    "x11-authority": "/run/user/john/dcv/tmp1387816934708382026.xauth",
    "num-of-connections": 1,
    "creation-time": "2019-05-14T14:12:14.989287Z",
    "last-disconnection-time": "2019-05-31T18:58:42.851223Z",
    "days-idle": 3
  },
  {
    "id": "tmp4241942441012516520",
    "owner": "mike",
    "x11-display": ":10",
    "x11-authority": "/run/user/mike/dcv/tmp4241942441012516520.xauth",
    "num-of-connections": 0,
    "creation-time": "2019-05-17T16:23:05.891531Z",
    "last-disconnection-time": "2019-05-19T11:23:30.844797Z",
    "days-idle": 3
  }
]

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .