jq

Case-insensitive check in `jq` for equality


I found the following on the govc govmomi github pages, which allows me to search for VMs by MAC address:

govc object.collect -json -type m / config.hardware.device \
  | jq -r '
      .
      | select(.ChangeSet[].Val.VirtualDevice[].MacAddress == "00:50:56:bc:5e:3c")
      | [.Obj.Type, .Obj.Value]
      | join(":")
    ' \
  | xargs govc ls -L

Please find attached a snippet of govc output that should suffice:

{
  "Obj": {
    "Type": "VirtualMachine",
    "Value": "vm-666"
  },
  "ChangeSet": [
    {
      "Val": {
        "VirtualDevice": [
          {
          },
          {
            "MacAddress": "00:50:56:bc:5e:3c"
          }
        ]
      }
    }
  ]
}

The problem I'm facing is that some MACs contain upper, some lower case letters, and I don't know ahead of time which machine uses which scheme. I can't figure out how to make the == case insensitive.

I have found answers here on stackoverflow that suggest using ascii_downcase as a filter, but I don't know how to apply that w/ the ==.

The syntax suggested at https://stackoverflow.com/a/62454688 gives me a sequence of explode input must be a string errors (1 per line of govc output).

In case this matters for this exercise: this is happening on Mac OS 15.1, jq is installed via brew, version is 1.7.1.


Solution

  • it outputs the names of every vm, whether they have that MAC or not ...

    Each ā€¦[] in .ChangeSet[].Val.VirtualDevice[].MacAddress creates a stream of potentially more than one result. In the sample given, .ChangeSet[] produces one item, and (for each of them) .Val.VirtualDevice evaluates to two items, creating a combined stream of two items. If you want to reduce this stream to a binary decision (zero or one items only), use an aggregate function that performs this conversion. For example, if you want select to return with one item if at least one of them passes the test, use the any function.

    (Also, I'm preferring strings over // "", so items without a .MacAddress are filtered out immediately instead of replacing them with an empty string that only fails after when compared to another literal string.)

    select(any(
      .ChangeSet[].Val.VirtualDevice[].MacAddress | strings;
      ascii_downcase == "00:50:56:bc:5e:3c"
    ))
    | [.Obj.Type, .Obj.Value]
    | join(":")
    
    VirtualMachine:vm-666
    

    Demo