Vaccano
Vaccano

Reputation: 82467

Print from jq using a wild card (or coalesce to first non null)

I have the following command:

kubectl get pod -A -o=json | jq -r '.items[]|select(any( .status.containerStatuses[]; .state.waiting or .state.terminated))|[.metadata.namespace, .metadata.name]|@csv'

This command works great. It outputs both the namespace and name of my failing pods.

But now I want to add one more column to the results. The column I want is located in one (and only one) of two places:

I first tried adding .status.containerStatuses[].state.*.reason to the results fields array. But that gave me an unexpected '*' compile error.

I then got to thinking about how I would do this with SQL or another programming language. They frequently have a function that will return the first non-null value of its parameters. (This is usually called coalesce). However I could not find any such command for jq.

How can I return the reason as a result of my query?

Upvotes: 11

Views: 4053

Answers (2)

peak
peak

Reputation: 116919

In case you want to go with coalesce:

# Stream-oriented version
def coalesce(s):
  first(s | select(. != null)) // null;

or if you prefer to work with arrays:

# Input: an array
# Output: the first non-null element if any, else null
def coalesce: coalesce(.[]);

Using the stream-oriented version, you could write something along the lines you had in mind with the wildcard, e.g.

coalesce(.status.containerStatuses[].state[].reason?)

Upvotes: 1

peak
peak

Reputation: 116919

jq has a counterpart to "coalesce" in the form of //. For example, null // 0 evaluates to 0, and chances are that it will suffice in your case, perhaps:

.status.containerStatuses[].state | (.waiting // .terminated) | .reason

or

.status.containerStatuses[].state | (.waiting.reason // .terminated.reason )

or similar.

However, // should only be used with some understanding of what it does, as explained in detail on the jq FAQ at https://github.com/stedolan/jq/wiki/FAQ#or-versus-

If // is inapplicable for some reason, then the obvious alternative would be an if ... then ... else ... end statement, which is quite like C's _ ? _ : _ in that it can be used to produce a value, e.g. something along the lines of:

.status.containerStatuses[].state
| if has("waiting") then .waiting.reason
  else .terminated.reason
  end

However, if containerStatuses is an array, then some care may be required.

Upvotes: 15

Related Questions