Como posso usar um “documento aqui” no meio de um cano?

Como posso usar um “documento aqui” no meio de um cano?

Estou querendo gerar algum conteúdo usando um heredoc como modelo:

passphrase=$(<passphrase) envsubst <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: openshift-passphrase
stringData:
  passphrase: ${passphrase}
EOF

e canalize para oc create -f -.

Se eu adicionar um pipe após o EOF, não funcionará.

Como posso canalizar um heredoc com substituição de variável para algo que o consome?

Responder1

Primeiro você precisa citar qualquer parte EOFlogo depois <<. A maneira mais natural é <<"EOF", mas <<E"OF"ou até <<""EOFservirá. Sem isso envsubsta string já estará ${passphrase}expandida. Como envsubstopera em strings com literais $fooou ${foo}substrings, expandi-las antecipadamente significa que envsubstnão há nada a ver. Além disso, no seu caso, o shell provavelmente se expandirá ${passphrase}para uma string vazia, porque a definição da variável no seu código afeta apenas envsubst, não o próprio shell; a menos que a variável com o mesmo nome seja (acidentalmente?) definida no shell de antemão.

Agora chegamos à sua pergunta explícita. Você pode canalizar o resultado para qualquer comando que desejar, mas ainda precisa manter o EOF final em uma linha separada. Uma maneira de fazer isso é assim:

passphrase=$(<passphrase) envsubst <<"EOF" | oc create -f -
apiVersion: v1
kind: Secret
metadata:
  name: openshift-passphrase
stringData:
  passphrase: ${passphrase}
EOF

Ou você pode executar o código que possui em um subshell:

( passphrase=$(<passphrase) envsubst <<"EOF"
apiVersion: v1
kind: Secret
metadata:
  name: openshift-passphrase
stringData:
  passphrase: ${passphrase}
EOF
) | oc create -f -

ObservaçãoManual de referência do Bashdiz

Cada comando em um pipeline é executado em seu próprio subshell

então, mesmo na primeira solução, quando conseguimos construir nosso pipe sem ( ), sua primeira parte (antes |) é executada em um subshell de qualquer maneira. A segunda solução torna este subshell explícito. Depois de usarmos explicit (, o shell espera por explicit ). Isso nos permite colocar algo após o término EOF.

Surpreendentemente, mesmo com a primeira solução você pode usar mais de um documento aqui ( <<) em um único comando composto. Esses redirecionamentos fazem pouco sentido em um canal, mas podem ser úteis com &&e ||.

command1 <<EOF && command2 <<EOF || command3 <<EOF
content1
EOF
content2
EOF
content3
EOF

O mesmo reorganizado, com subcamadas explícitas:

( command1 <<EOF
content1
EOF
) && ( command2 <<EOF
content2
EOF
) || command3 <<EOF
content3
EOF

Dependendo da situação, você pode preferir uma notação a outra.

De volta ao seu exemplo específico. Com um subshell você nem precisa envsubst:

( passphrase=$(<passphrase); oc create -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: openshift-passphrase
stringData:
  passphrase: ${passphrase}
EOF
)

Existem diferenças interessantes entre esta forma e as duas anteriores:

  • Desta vez, o próprio subshell deve se expandir ${passphrase}e, portanto <<EOF, não <<"EOF".
  • Para que isso funcione, a variável deve ser conhecida pelo subshell, não apenas por oc; isso significa … passphrase=$(<passphrase) oc create -f - <<…(observe a falta de ponto e vírgula) não funcionaria.
  • Tecnicamente, o mesmo código que não está em um subshell (ou seja, sem ( )) também funcionaria, mas a variável permaneceria no shell principal. Executar o código em um subshell faz com que a variável morra com ele. No seu código original, a variável não está definida para o shell principal, então acho que é isso que você deseja.

informação relacionada