使用 /dev/stdin 和 heredoc 从命令行传递文件

mbi*_*ras 4 command-line bash here-document ansible

我很好奇 heredocs 如何作为文件传递给命令行实用程序背后的理论。

最近,我发现我可以将文件作为 heredoc 传递。

例如:

awk '{ split($0, arr, " "); print arr[2] }' <<EOF
foo bar baz
EOF
bar
Run Code Online (Sandbox Code Playgroud)

出于以下几个原因,这对我来说是有利的:

  • Heredocs 提高了多行输入的可读性。
  • 我不需要记住每个实用程序标志来从命令行传递文件内容。
  • 我可以在给定的文件中使用单引号和双引号。
  • 我可以控制外壳扩展。

例如:

ruby <<EOF
puts "'hello $HOME'"
EOF
'hello /Users/mbigras'

ruby <<'EOF'
puts "'hello $HOME'"
EOF
'hello $HOME'
Run Code Online (Sandbox Code Playgroud)

我不清楚发生了什么。似乎shell认为heredoc是一个内容等于heredoc值的文件。我已经将这种技术与 cat 一起使用,但我仍然不确定发生了什么:

cat <<EOL
hello world
EOL
hello world
Run Code Online (Sandbox Code Playgroud)

我知道cat打印文件的内容,所以大概这个 heredoc 是某种临时文件。

当我“将heredoc传递给命令行程序”时,我对究竟发生了什么感到困惑。

这是使用ansible-playbook的示例。我将实用程序作为 heredoc 传递给剧本;但是它失败了,如使用所示echo $?

ansible-playbook -i localhost, -c local <<EOF &>/dev/null
---
- hosts: all
  gather_facts: false
  tasks:
    - name: Print something
      debug:
        msg: hello world
EOF
echo $?
5
Run Code Online (Sandbox Code Playgroud)

但是,如果我通过该实用程序相同的 heredoc 但在它/dev/stdin之前成功

ansible-playbook -i localhost, -c local /dev/stdin <<EOF &>/dev/null
---
- hosts: all
  gather_facts: false
  tasks:
    - name: Print something
      debug:
        msg: hello world
EOF
echo $?
0
Run Code Online (Sandbox Code Playgroud)
  • 当一个人“将heredoc作为文件传递”时,究竟发生了什么?
  • 为什么第一个版本ansible-playbook失败而第二个版本成功?
  • /dev/stdin在heredoc之前通过有什么意义?
  • 为什么其他实用程序喜欢rubyawk不需要/dev/stdin在 heredoc 之前?

Mic*_*mer 9

当一个人“将heredoc作为文件传递”时究竟发生了什么?

你不是。Here-documents 提供标准输入,就像一个管道。你的榜样

awk '{ ... }' <<EOF
foo bar baz
EOF
Run Code Online (Sandbox Code Playgroud)

完全等同于

echo foo bar baz | awk '{ ... }'
Run Code Online (Sandbox Code Playgroud)

awk, cat, 和ruby所有从标准输入读取,如果它们没有在命令行上读取的文件名。这是一个实现选择。

为什么带有 anisble-playbook 的第一个版本失败而第二个版本成功?

ansible-playbook默认情况下不从标准输入读取,而是需要文件路径。这是一种设计选择。

/dev/stdin很可能是指向 的符号链接/dev/fd/0,这是一种谈论当前进程的文件描述符#0(标准输入)的方式。这是您的内核(或系统库)公开的内容。该ansible-playbook命令/dev/stdin像常规文件系统文件一样打开并最终读取它自己的标准输入,否则这些输入将被忽略。

您可能也有/dev/stdout/dev/stderr链接的FD 1&2,你可以,如果你使用,以及在告诉一些地方把它的输出。

在heredoc之前传递/dev/stdin有什么意义?

它是ansible-playbook命令的参数。

为什么像 ruby​​ 或 awk 这样的其他实用程序在 heredoc 之前不需要 /dev/stdin?

默认情况下,它们从标准输入中读取作为设计选择,因为它们是为在管道中使用而设计的。他们出于同样的原因写入标准输出。