如何在管道中间使用“此处文档”?

sim*_*905 3 linux bash command-line sh pipe

我想使用heredoc作为模板生成一些内容:

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

并将其通过管道传输到oc create -f -.

如果我在 后面添加管道EOF,则不起作用。

如何通过管道将带有变量替换的定界文档传递给消耗它的东西?

Kam*_*ski 7

首先,您需要引用EOF之后的任何部分<<。最自然的方式是<<"EOF",但是<<E"OF"或者甚至<<""EOF可以。如果没有这个,envsubst将会得到已经扩展的字符串${passphrase}。由于对带有文字或子字符串的envsubst字符串进行操作,因此预先扩展它们意味着无关。此外,在您的情况下,shell 很可能会扩展为空字符串,因为代码中的变量定义仅影响,而不影响 shell 本身;除非事先在 shell 中设置了同名变量(不小心?)。$foo${foo}envsubst${passphrase}envsubst

\n\n

现在我们来回答你的明确问题。您可以将结果通过管道传输到您想要的任何命令,但您仍然需要将最终的 EOF 保留在单独的行中。一种方法是这样的:

\n\n
passphrase=$(<passphrase) envsubst <<"EOF" | oc create -f -\napiVersion: v1\nkind: Secret\nmetadata:\n  name: openshift-passphrase\nstringData:\n  passphrase: ${passphrase}\nEOF\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者您可以在子 shell 中运行代码:

\n\n
( passphrase=$(<passphrase) envsubst <<"EOF"\napiVersion: v1\nkind: Secret\nmetadata:\n  name: openshift-passphrase\nstringData:\n  passphrase: ${passphrase}\nEOF\n) | oc create -f -\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意Bash 参考手册

\n\n
\n

管道中的每个命令都在其自己的子 shell 中执行

\n
\n\n

因此,即使在第一个解决方案中,当我们设法在没有 的情况下构建管道时( ),它的第一部分(在 之前|)无论如何都会在子 shell 中运行。第二种解决方案使这个子 shell 变得明确。使用explicit后(,shell会等待explicit )。这允许我们在终止之后放置一些东西EOF

\n\n

<<令人惊讶的是,即使使用第一个解决方案,您也可以在单个复合命令中使用多个此处文档 ( )。这种重定向在管道中没有什么意义,但它们对于&&和可能很有用||

\n\n
command1 <<EOF && command2 <<EOF || command3 <<EOF\ncontent1\nEOF\ncontent2\nEOF\ncontent3\nEOF\n
Run Code Online (Sandbox Code Playgroud)\n\n

相同的重新排列,具有明确的子 shell:

\n\n
( command1 <<EOF\ncontent1\nEOF\n) && ( command2 <<EOF\ncontent2\nEOF\n) || command3 <<EOF\ncontent3\nEOF\n
Run Code Online (Sandbox Code Playgroud)\n\n

根据情况,您可能更喜欢其中一种表示法。

\n\n

回到你的具体例子。使用子外壳,您甚至不需要envsubst

\n\n
( passphrase=$(<passphrase); oc create -f - <<EOF\napiVersion: v1\nkind: Secret\nmetadata:\n  name: openshift-passphrase\nstringData:\n  passphrase: ${passphrase}\nEOF\n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这种方式与前两种方式有一些有趣的区别:

\n\n
    \n
  • 这次子 shell 本身应该扩展${passphrase},因此<<EOF不会<<"EOF"
  • \n
  • 为此,子 shell 必须知道该变量,而不仅仅是oc; 这意味着\xe2\x80\xa6 passphrase=$(<passphrase) oc create -f - <<\xe2\x80\xa6(注意缺少分号)不起作用。
  • \n
  • 从技术上讲,不在子 shell 中(即没有( ))的相同代码也可以工作,但变量将保留在主 shell 中。在子 shell 中运行代码会使变量随之消失。在您的原始代码中,未为主 shell 设置变量,所以我想这就是您想要的。
  • \n
\n