Pipe command output inside other command

Viewed 199

I'm trying to pipe the output of the first command into the quotation marks at the end of the second command.

kubectl --context foo -n foo get secret postgres.foo-db.credentials -o jsonpath={.data.password}

kubectl --context foo -n foo patch secret postgres.foo-db.credentials -p '{"data":{"password":"    Output from command 1    "}}'

I've already tried this: piping output into middle of bash command, but I get the following error with this command:

kubectl --context foo -n foo patch secret postgres.foo-db.credentials -p '{"data":{"`password":"kubectl --context foo -n foo get secret postgres.foo-db.credentials -o jsonpath={.data.password}`"}}'

The request is invalid: patch: Invalid value: "map[data:map[`password:kubectl --context foo  -n foo  get secret postgres.foo-db.credentials -o jsonpath={.data.password}`]]": error decoding from json: illegal base64 data at input byte 7
3 Answers

You can split the command in two to make it easier to understand and let the bash shell expand the variable pgsecret in the second line:

pgsecret="$(kubectl --context foo -n foo get secret \
  postgres.foo-db.credentials -o jsonpath={.data.password})"
kubectl \
  --context foo -n foo patch secret \
  postgres.foo-db.credentials -p '{"data":{"password":"'$pgsecret'"}}'

The idea of Davide Madrisan is good. The code will be more readable. But with this solution you could probably receive an error. In the command:

kubectl --context foo -n foo patch secret postgres.foo-db.credentials -p '{"data":{"password":"'$pgsecret'"}}'

the reference to a variable "'$pgsecret'" is with the double and single quotes. Double are fine, because it must be "key":"value" structure. But on my environment single quotes were wrong. I have received an error:

Error from server (BadRequest): invalid character '<wrong character>' after object key:value pair

You can read more about the difference between ' and " here.

I suggest to remove single quotes:

pgsecret="$(kubectl --context foo -n foo get secret \
  postgres.foo-db.credentials -o jsonpath={.data.password})"
kubectl \
  --context foo -n foo patch secret \
  postgres.foo-db.credentials -p '{"data":{"password":"$pgsecret"}}'

or if you want to do this in the one line without a variable (less readable code):

kubectl --context foo -n foo patch secret postgres.foo-db.credentials -p '{"data":{"password":"$(kubectl --context foo -n foo get secret postgres.foo-db.credentials -o jsonpath={.data.password})"}}'

should also work with you. This treatment is called command substitution, as user1934428 well mentioned in the comment:

A pipe won't do here. You need command substitution, because one part of your command is supposed to be substituted by the standard output of a different command.

backticks (aka $()) are not used as command substitution inside single quotes, but are literal:

$ echo '$(echo foo)'
$(echo foo)

They are interpolated inside double quotes:

$ echo "$(echo foo)"
foo

So you just need to properly quote your substitution:

$ kubectl --context ... -p '{"data":{"password":"'"$(cmd to get passwd)"'"}}'

You could also write:

$ kubectl --context ... -p "{\"data\":{\"password\":\"$(cmd to get passwd)\"}}"
Related