"猫<< EOF"如何在bash中运作?

has*_*sen 548 linux bash scripting heredoc

我需要编写一个脚本来输入程序的多行输入(psql).

经过一段谷歌搜索后,我发现以下语法有效:

cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF
Run Code Online (Sandbox Code Playgroud)

这正确地构造了多行字符串(从包含BEGIN;END;包含),并将其作为输入进行管道输入psql.

但是我不知道它是如何/为什么有效的,有人可以解释一下吗?

我主要是指cat << EOF,我知道>文件的输出,>>附加到文件,<从文件读取输入.

究竟做<<了什么?

它有一个手册页吗?

ken*_*ytm 473

这称为heredoc格式,用于将字符串提供给stdin.有关详细信息,请参阅https://en.wikipedia.org/wiki/Here_document#Unix_shells.


来自man bash:

这里的文件

这种类型的重定向指示shell从当前源读取输入,直到看到只包含单词(没有尾随空白)的行.

然后,读取到该点的所有行都将用作命令的标准输入.

here-documents的格式是:

          <<[-]word
                  here-document
          delimiter
Run Code Online (Sandbox Code Playgroud)

不对执行参数扩展,命令替换,算术扩展或路径名扩展 .如果引用单词中的任何字符,则 分隔符单词上的引号删除的结果,并且不会展开here-document中的行.如果word不加引号,则here-document的所有行 都要进行参数扩展,命令替换和算术扩展.在后一种情况下,字符序列\<newline>被忽略,\必须使用来引用字符\,$`.

如果是重定向运算符<<-,则从输入行和包含分隔符的行中删除所有前导制表.这允许shell脚本中的文档以自然的方式缩进.

  • 我最困难的时候是禁用变量/参数扩展.我需要做的就是使用"双引号"并修复它!谢谢(你的)信息! (12认同)
  • 关于`<< - `请注意,只有前导*tab*字符被剥离 - 而不是软标签字符.当您确实需要制表符时,这是极少数情况之一.如果文档的其余部分使用软选项卡,请确保显示不可见的字符,并(例如)复制并粘贴制表符.如果你做得对,你的语法高亮应该正确捕捉结束分隔符. (10认同)
  • 我不明白这个答案比下面的答案更有帮助。它只是重复可以在其他地方找到的信息(可能已经被检查过) (2认同)

Voj*_*tek 448

cat <<EOF在Bash中处理多行文本时,语法非常有用,例如.将多行字符串分配给shell变量,文件或管道时.

cat <<EOFBash 中语法用法的示例:

1.将多行字符串分配给shell变量

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)
Run Code Online (Sandbox Code Playgroud)

$sql变量现在也包含换行符.你可以验证echo -e "$sql".

2.将多行字符串传递给Bash中的文件

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
Run Code Online (Sandbox Code Playgroud)

print.sh文件现在包含:

#!/bin/bash
echo $PWD
echo /home/user
Run Code Online (Sandbox Code Playgroud)

3.将多行字符串传递给Bash中的管道

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF
Run Code Online (Sandbox Code Playgroud)

b.txt文件包含barbaz行.打印相同的输出stdout.

  • 为什么这个答案有任何赞成票?你完全忽略了这个问题,即“它如何/为什么起作用”。没有人要求提供随机示例列表,但不知何故,它的赞成票数比(正确)接受的答案还要多。 (28认同)
  • @EntangledLoops 当我添加这些额外的示例时,已经有一个可接受的答案。从点赞数来看,它们非常有用。和平。 (9认同)
  • 1. 1和3无需cat即可完成;2. 示例1可以用简单的多行字符串来完成 (3认同)
  • 与其使用“cat”创建另一个进程,为什么不使用 IFS='' read -r -d 呢? (2认同)

ede*_*ans 227

在您的情况下,"EOF"被称为"Here Tag".基本上<<Here告诉shell你要输入一个多行字符串,直到"标签" Here.您可以根据需要为此标记命名,通常是EOFSTOP.

有关Here标签的一些规则:

  1. 标签可以是任何字符串,大写或小写,但大多数人按惯例使用大写字母.
  2. 如果该行中还有其他单词,则不会将该标记视为Here标记.在这种情况下,它将仅被视为字符串的一部分.标签应该单独在一条线上,被视为标签.
  3. 标记应该没有该行中的前导或尾随空格才能被视为标记.否则它将被视为字符串的一部分.

例:

$ cat >> test <<HERE
> Hello world HERE <-- Not by itself on a separate line -> not considered end of string
> This is a test
>  HERE <-- Leading space, so not considered end of string
> and a new line
> HERE <-- Now we have the end of the string
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的实际答案......你定义两者并明确说明使用的主要目的而不是相关理论......这很重要但不是必要的...谢谢 - 超级有帮助 (19认同)
  • @edelans,您必须补充一点,当使用`&lt;&lt;-`时,前导选项卡不会阻止标签被识别 (4认同)

Cir*_*四事件 69

POSIX 7

kennytm引用man bash,但其中大部分也是POSIX 7:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

重定向运算符"<<"和"<< - "都允许将包含在shell输入文件中的行(称为"here-document")重定向到命令的输入.

here-document应被视为一个单词,它在下一个文本之后开始,并一直持续到一行只包含分隔符和a,其间没有字符.然后下一个here-document开始,如果有的话.格式如下:

[n]<<word
    here-document
delimiter
Run Code Online (Sandbox Code Playgroud)

其中可选的n表示文件描述符号.如果省略该数字,则here-document指标准输入(文件描述符0).

如果引用单词中的任何字符,则应通过对单词执行引用删除来形成分隔符,并且不应扩展此文档行.否则,分隔符应为单词本身.

如果引用单词中没有字符,则应扩展here-document的所有行以进行参数扩展,命令替换和算术扩展.在这种情况下,输入中的行为与内部双引号相同(请参阅双引号).但是,双引号字符('"')不应在here-document中特别处理,除非双引号出现在"$()","`"或"$ {}"中.

如果重定向符号是"<< - ",则所有前导<tab>字符都应从输入行和包含尾随分隔符的行中剥离.如果在一行上指定了多个"<<"或"<< - "运算符,则与第一个运算符关联的here-document应首先由应用程序提供,并且应首先由shell读取.

当从终端设备读取here-document并且shell是交互式的时,它应该在读取每行输入之前将变量PS2的内容(如Shell变量中所述进行处理)写入标准错误,直到识别出分隔符.

例子

一些尚未给出的例子.

报价可防止参数扩展

没有引号:

a=0
cat <<EOF
$a
EOF
Run Code Online (Sandbox Code Playgroud)

输出:

0
Run Code Online (Sandbox Code Playgroud)

报价:

a=0
cat <<'EOF'
$a
EOF
Run Code Online (Sandbox Code Playgroud)

或(丑陋但有效):

a=0
cat <<E"O"F
$a
EOF
Run Code Online (Sandbox Code Playgroud)

输出:

$a
Run Code Online (Sandbox Code Playgroud)

连字符删除了前导标签

没有连字符:

cat <<EOF
<tab>a
EOF
Run Code Online (Sandbox Code Playgroud)

其中<tab>是文字标签,可以插入Ctrl + V <tab>

输出:

<tab>a
Run Code Online (Sandbox Code Playgroud)

用连字符:

cat <<-EOF
<tab>a
<tab>EOF
Run Code Online (Sandbox Code Playgroud)

输出:

a
Run Code Online (Sandbox Code Playgroud)

这当然存在,以便您可以cat像周围的代码一样缩进,这更容易阅读和维护.例如:

if true; then
    cat <<-EOF
    a
    EOF
fi
Run Code Online (Sandbox Code Playgroud)

不幸的是,这对空格字符不起作用:POSIX赞成tab缩进.让人惊讶.

  • ...只需在 `$` 前面使用反斜杠 (2认同)

Chr*_*row 36

<< EoF基本上意味着:

<<- “读取从下一行开始的多行输入,并将其视为单独文件中的代码”

EoFEoF- “在多行输入中找到单词后立即停止阅读”

正如其他答案所解释的那样,多行输入称为此处文档(即它是“内联”或“现场”提供的文档)。

Here Document 通常用于生成要传递到后续流程的输出。例如,cat << EoF可用于使用此处文档生成所需的输出。

以下是使用此处文档动态创建文本文档的示例:

cat << EoF > ./my-document.txt
Hello world
Have a nice day
EoF
Run Code Online (Sandbox Code Playgroud)


And*_*ier 23

用发球而不是猫

不完全是对原始问题的回答,但我还是想分享这个:我需要在需要root权限的目录中创建配置文件.

以下内容不适用于该情况:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF
Run Code Online (Sandbox Code Playgroud)

因为重定向是在sudo上下文之外处理的.

我最终使用了这个:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
Run Code Online (Sandbox Code Playgroud)


Lef*_*ogh 11

对上述答案的一点扩展。尾随>将输入定向到文件中,覆盖现有内容。但是,一种特别方便的用法是>>附加的双箭头,将新内容添加到文件末尾,如下所示:

cat <<EOF >> /etc/fstab
data_server:/var/sharedServer/authority/cert /var/sharedFolder/sometin/authority/cert nfs
data_server:/var/sharedServer/cert   /var/sharedFolder/sometin/vsdc/cert nfs
EOF
Run Code Online (Sandbox Code Playgroud)

这扩展了您,fstab而您不必担心意外修改其任何内容。


dwi*_*nto 11

请注意, cat << \EOT (参见反斜杠)不会扩展内部的任何变量,而 cat << EOT 可以。

例子:

FOO="bar"

cat << \EOT > foobar.txt
echo "$FOO"
EOT
Run Code Online (Sandbox Code Playgroud)

将输出: echo $FOO

尽管:

FOO="bar"

cat << EOT > foobar.txt
echo "$FOO"
EOT
Run Code Online (Sandbox Code Playgroud)

将输出:echo“bar”


Ces*_*lis 9

创建 json 文件的示例:

cat << EoF > ./allaccess.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "arn:aws:s3:::*"
      ]
    }
  ]
}
EoF
Run Code Online (Sandbox Code Playgroud)

因此:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "arn:aws:s3:::*"
      ]
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)