Home > Mobile >  How can I iterate over a shell array of objects and delete them from a JSON file with jq?
How can I iterate over a shell array of objects and delete them from a JSON file with jq?

Time:11-05

I'm trying to cleanup inactive channels from a list of json objects in a channels.json file from Slack. I got the list of inactive channels (defined as not updated in last 90 days) by checking the timestamp of the last message in the channel directory in my export and writing the list of inactive channels to an array as this data is not available in the json objects themselves. Only problem is I don't know how to arrange it so that each of the channels in my array are removed from the input before writing to a new output file. See the missing something important here comment in my function code below.

Here's my current function.

exclude-inactive-channels () {
# Check for a valid argument
  if [ -z $1 ]; then
    echo "No arguments supplied."
    return 1
  elif [ $# -gt 1 ]; then
    echo "Too many arguments supplied."
    return 1
  elif [ ! -f $1 ]; then
    echo "File doesn't exist."
    return 1
  fi

  cutoff_epoch=$(gdate  -d "90 days ago"  '%s')
  inactive_channels=()
  for channel in $(jq -r '.[] | select(.is_archived == false) | .name' $1); do 
    if [[ -d $channel ]]; then
      last_post=$(ls -1 $channel |sort -n |tail -1 |awk -F'.' '{print $1}')
      last_post_epoch=$(gdate -d "$last_post"  '%s')
      if [[ $last_post_epoch -lt $cutoff_epoch ]]; then
          inactive_channels =("$channel")
          echo -n "Removing $channel directory. Last post is $last_post."
          #rm -rf $channel
          echo -e "\033[0;32m[OK]\033[0m"
      fi
    fi
  done

  echo "Removing inactive channels from $1 and writing output to new-$1."
  for inactive_channel in ${inactive_channels[@]}; do
    # Next line is untested pseudo code
    jq -r '.[] | del(.name == $inactive_channel)' $1 #missing something important here
  done | jq -s > new-${1}

  echo "Replacing $1 with new-$1."
  # mv new-${1} $1
}

Calling this function:

exclude-inactive-channels channels.json

Example Input:

[
  {
    "id": "",
    "name": "announcements",
    "created": 1500000000,
    "creator": "",
    "is_archived": false,
    "is_general": true,
    "members": [
      "",
  ],
    "pins": [
      {
        "id": "",
        "type": "C",
        "created": 1500000000,
        "user": "",
        "owner": ""
      },
      ],
    "topic": {
      "value": "",
      "creator": "",
      "last_set": 0
    },
    "purpose": {
      "value": "company wide announcements",
      "creator": "",
      "last_set": 1500000000
    }
  },
  {
    "id": "",
    "name": "general",
    "created": 1500000000,
    "creator": "",
    "is_archived": false,
    "is_general": true,
    "members": [
      "",
  ],
    "pins": [
      {
        "id": "",
        "type": "C",
        "created": 1500000000,
        "user": "",
        "owner": ""
      },
      ],
    "topic": {
      "value": "",
      "creator": "",
      "last_set": 0
    },
    "purpose": {
      "value": "general",
      "creator": "",
      "last_set": 1500000000
    }
  },
]

CodePudding user response:

More efficient is to feed jq all your channels to delete at once, rather than one-at-a-time.

# you need to comment one of these out for out.json to not be an empty list
inactive_channels=(
  "announcements"
  "general"
)

jq --rawfile inactive_channel_stream <(printf '%s\0' "${inactive_channels[@]}") '
  # generate an object mapping keys to delete to a truthy value
  INDEX($inactive_channel_stream | split("\u0000")[]; .) as $inactive_channels

  | map(select(($inactive_channels[.name] // false) | not))
' <in.json >out.json
  • Related