bash中的“ cat << EOF”如何工作?

情到浓时终转凉″ 提交于 2020-02-26 07:28:27

我需要编写脚本以将多行输入输入到程序( psql )。

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

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

这样可以正确构造多行字符串(从BEGIN;END; ,包括两端),并将其作为输入传递给psql

但是我不知道它如何/为什么起作用,请解释一下吗?

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

<<到底是做什么的?

并且有手册页吗?


#1楼

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

Bash中cat <<EOF语法用法的示例:

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


#2楼

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


来自man bash

这里文件

这种重定向指示外壳程序从当前源读取输入,直到看到仅包含单词(无尾随空格)的行。

直到该点为止的所有行都将用作命令的标准输入。

此处文档的格式为:

 <<[-]word here-document delimiter

没有对word执行参数扩展,命令替换,算术扩展或路径名扩展。 如果对word中的任何字符加引号,则定界符是对word删除引号的结果,并且本文档中的行不会扩展。 如果未引用word ,则此文档的所有行都将进行参数扩展,命令替换和算术扩展。 在后一种情况下,字符序列\\<newline>被忽略,并且必须使用\\来引用字符\\$`

如果重定向运算符是<<- ,则从输入行和包含定界符的行中除去所有前导制表 。 这允许外壳脚本中的此处文档以自然方式缩进。


#3楼

在您的情况下,“ EOF”被称为“此处标签”。 基本上, <<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

#4楼

POSIX 7

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

重定向操作符“ <<”和“ <<-”都允许将外壳程序输入文件(称为“ here-document”)中包含的行重定向到命令的输入。

此处文档应被视为一个单词,该单词从下一个单词开始,一直持续到一行仅包含定界符和a,且中间没有字符。 然后,下一个此处文档开始(如果有)。 格式如下:

[n]<<word here-document delimiter

其中可选的n表示文件描述符号。 如果省略该数字,则本文参考标准输入(文件描述符0)。

如果在单词中引用了任何字符,则应通过对单词执行引号删除来形成分隔符,并且此处文档行不得扩展。 否则,定界符应为单词本身。

如果单词中没有引用任何字符,则应扩展本文档的所有行,以进行参数扩展,命令替换和算术扩展。 在这种情况下,输入中的in表现为内部双引号(请参见Double-Quotes)。 但是,除非在“ $()”,“``''或“ $ {}”中出现双引号,否则本文中的双引号字符('“')不应作特殊处理。

如果重定向符号为“ <<-”,则应从输入行和包含尾部定界符的行中删除所有前导的<tab>字符。 如果在一行上指定了多个“ <<”或“ <<-”运算符,则与第一个运算符关联的此处文档应由应用程序首先提供,并由外壳程序首先读取。

当从终端设备读取此处文档并且外壳是交互式的时,它将在读取输入的每一行之前将按外壳变量中所述进行处理的变量PS2的内容写入标准错误,直到识别出定界符为止。

例子

一些例子尚未给出。

引号防止参数扩展

不带引号:

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

其中<tab>是文字选项卡,可以使用Ctrl + V <tab>插入

输出:

<tab>a

带连字符:

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

输出:

a

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

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

不幸的是,这不适用于空格字符:POSIX倾向于在此处使用tab缩进。 kes。


#5楼

使用tee代替cat

并非完全是对原始问题的答案,但是我还是想分享一下:我需要在需要根权限的目录中创建配置文件。

以下情况不适用于这种情况:

$ 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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!