835

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

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

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

这正确地构造了多行字符串(从BEGIN;END;,包括在内)并将其作为输入传递给psql.

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

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

具体是做什么<<的?

是否有它的手册页?

4

11 回答 11

679

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

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

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

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

$sql变量现在也包含换行符。您可以使用 进行验证echo -e "$sql"

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

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

print.sh文件现在包含:

#!/bin/bash
echo $PWD
echo /home/user

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

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

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

于 2014-02-04T10:28:37.200 回答
630

这称为heredoc格式,用于将字符串提供到标准输入中。有关更多详细信息,请参阅https://en.wikipedia.org/wiki/Here_document#Unix_shells


来自man bash

这里的文件

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

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

这里文档的格式是:

          <<[-]word
                  here-document
          delimiter

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

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

于 2010-03-23T13:58:41.800 回答
392

在您的情况下,“EOF”被称为“Here Tag”。基本上<<Here告诉外壳你要输入一个多行字符串,直到 "tag" 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
于 2014-08-22T08:48:39.433 回答
119

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 应被视为一个单词,从下一个单词开始<newline>,一直持续到有一行只包含分隔符和 a <newline>,中间没有<blank>字符。然后下一个 here-document 开始,如果有的话。格式如下:

[n]<<word
    here-document
delimiter

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

如果 word 中的任何字符被引用,则应通过对 word 进行引号去除形成分隔符,并且不应扩展 here-document 行。否则,分隔符应为单词本身。

如果 word 中没有字符被引用,则 here-document 的所有行都应进行扩展,以进行参数扩展、命令替换和算术扩展。在这种情况下,<backslash>输入中的 in 表现为<backslash>内部双引号(请参阅双引号)。然而,双引号字符 ('"' ) 不应在 here-document 中特殊处理,除非双引号出现在 "$()"、"``" 或 "${}" 中。

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

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

例子

一些例子还没有给出。

引号防止参数扩展

不带引号:

a=0
cat <<EOF
$a
EOF

输出:

0

带引号:

a=0
cat <<'EOF'
$a
EOF

或(丑陋但有效):

a=0
cat <<E"O"F
$a
EOF

输出:

$a

连字符删除前导标签

没有连字符:

cat <<EOF
<tab>a
EOF

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

输出:

<tab>a

带连字符:

cat <<-EOF
<tab>a
<tab>EOF

输出:

a

这当然存在,因此您可以缩进您cat喜欢的周围代码,这更易于阅读和维护。例如:

if true; then
    cat <<-EOF
    a
    EOF
fi

不幸的是,这不适用于空格字符:POSIXtab在这里喜欢缩进。哎呀。

于 2015-06-09T09:41:52.003 回答
30

使用 tee 代替 cat

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

以下不适用于这种情况:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF

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

我最终改用了这个:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
于 2017-02-13T12:14:06.903 回答
12

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

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

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

于 2020-01-29T10:25:48.617 回答
4

长话短说,EOF标记(但也可以使用不同的文字)是一种heredoc格式,允许您将输入作为多行提供。很多困惑来自cat它看起来的实际工作方式。您可以使用catwith>>>如下:

$ cat >> temp.txt
line 1
line 2

虽然cat可以在手动写入控制台时以这种方式使用,但如果我想以更具声明性的方式提供输入以便它可以被工具重用并保留缩进、空格等,这并不方便
。Heredoc 允许定义你的整个输入,就像您不使用stdin但在单独的文本编辑器中输入一样。这就是维基百科文章的意思:

它是源代码文件的一部分,被视为一个单独的文件。

于 2020-08-31T07:08:23.333 回答
4

<< EoF基本上意味着:

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

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

正如其他答案所解释的,多行输入称为Here Document

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

下面是使用 Here Document 动态创建文本文档的示例:

cat << EoF > ./my-document.txt
Hello world
Have a nice day
EoF
于 2022-01-19T22:21:05.883 回答
1

这不一定是对原始问题的回答,而是分享我自己测试的一些结果。这:

<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

将产生与以下相同的文件:

cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

所以,我看不出使用 cat 命令的意义。

于 2018-06-06T00:15:40.733 回答
1

请注意, cat << \EOT (请参阅反斜杠)不会扩展内部的任何变量,而 cat << EOT 会这样做。

例子:

FOO="bar"

cat << \EOT > foobar.txt
echo "$FOO"
EOT

将输出:echo $FOO

尽管:

FOO="bar"

cat << EOT > foobar.txt
echo "$FOO"
EOT

将输出: echo "bar"

于 2021-11-30T01:25:48.997 回答
0

值得注意的是,这里的文档也可以在 bash 循环中工作。此示例显示如何获取表的列列表:

export postgres_db_name='my_db'
export table_name='my_table_name'

# start copy 
while read -r c; do test -z "$c" || echo $table_name.$c , ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
SELECT column_name
FROM information_schema.columns
WHERE 1=1
AND table_schema = 'public'
AND table_name   =:'table_name'  ;
EOF
)
# stop copy , now paste straight into the bash shell ...

output: 
my_table_name.guid ,
my_table_name.id ,
my_table_name.level ,
my_table_name.seq ,

甚至没有新行

while read -r c; do test -z "$c" || echo $table_name.$c , | perl -ne 
's/\n//gm;print' ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
 SELECT column_name
 FROM information_schema.columns
 WHERE 1=1
 AND table_schema = 'public'
 AND table_name   =:'table_name'  ;
 EOF
 )

 # output: daily_issues.guid ,daily_issues.id ,daily_issues.level ,daily_issues.seq ,daily_issues.prio ,daily_issues.weight ,daily_issues.status ,daily_issues.category ,daily_issues.name ,daily_issues.description ,daily_issues.type ,daily_issues.owner
于 2018-08-14T15:11:59.907 回答