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 EOF
logo depois <<
. A maneira mais natural é <<"EOF"
, mas <<E"OF"
ou até <<""EOF
servirá. Sem isso envsubst
a string já estará ${passphrase}
expandida. Como envsubst
opera em strings com literais $foo
ou ${foo}
substrings, expandi-las antecipadamente significa que envsubst
nã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.