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

在谷歌搜索了一下之后,我发现下面的语法是有效的:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

这正确地构造了多行字符串(从BEGIN;to END;,包括在内)并将其作为输入管道输送到psql。

但是我不知道它是怎么工作的,有人能解释一下吗?

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

<<到底是做什么的?

它有手册页吗?


当前回答

在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文件包含bar和baz行。相同的输出被打印到stdout。

其他回答

这被称为heredoc格式,用于向stdin提供字符串。详情见https://en.wikipedia.org/wiki/Here_document#Unix_shells。


来自man bash:

Here Documents This type of redirection instructs the shell to read input from the current source until a line containing only word (with no trailing blanks) is seen. All of the lines read up to that point are then used as the standard input for a command. The format of here-documents is: <<[-]word here-document delimiter No parameter expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If any characters in word are quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion. In the latter case, the character sequence \<newline> is ignored, and \ must be used to quote the characters \, $, and `. If the redirection operator is <<-, then all leading tab characters are stripped from input lines and the line containing delimiter. This allows here-documents within shell scripts to be indented in a natural fashion.

在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文件包含bar和baz行。相同的输出被打印到stdout。

在你的例子中,“EOF”被称为“Here Tag”。基本上,<<Here告诉shell,您将输入一个多行字符串,直到“tag”在这里。你可以随意命名这个标签,通常是EOF或STOP。

关于Here标签的一些规则:

标签可以是任何字符串,大写或小写,尽管大多数人习惯使用大写。 如果该行中有其他单词,则该标记将不被视为Here标记。在这种情况下,它仅仅被认为是字符串的一部分。标签本身应该在单独的行中,被认为是一个标签。 标签在该行中不应该有前导或尾随空格,才能被视为标签。否则它将被视为字符串的一部分。

例子:

$ 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

POSIX 7

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

The redirection operators "<<" and "<<-" both allow redirection of lines contained in a shell input file, known as a "here-document", to the input of a command. The here-document shall be treated as a single word that begins after the next <newline> and continues until there is a line containing only the delimiter and a <newline>, with no <blank> characters in between. Then the next here-document starts, if there is one. The format is as follows: [n]<<word here-document delimiter where the optional n represents the file descriptor number. If the number is omitted, the here-document refers to standard input (file descriptor 0). If any character in word is quoted, the delimiter shall be formed by performing quote removal on word, and the here-document lines shall not be expanded. Otherwise, the delimiter shall be the word itself. If no characters in word are quoted, all lines of the here-document shall be expanded for parameter expansion, command substitution, and arithmetic expansion. In this case, the <backslash> in the input behaves as the <backslash> inside double-quotes (see Double-Quotes). However, the double-quote character ( '"' ) shall not be treated specially within a here-document, except when the double-quote appears within "$()", "``", or "${}". If the redirection symbol is "<<-", all leading <tab> characters shall be stripped from input lines and the line containing the trailing delimiter. If more than one "<<" or "<<-" operator is specified on a line, the here-document associated with the first operator shall be supplied first by the application and shall be read first by the shell. When a here-document is read from a terminal device and the shell is interactive, it shall write the contents of the variable PS2, processed as described in Shell Variables, to standard error before reading each line of input until the delimiter has been recognized.

例子

有些例子还没有给出。

引号阻止参数扩展

没有引用:

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偏爱制表符缩进。呵。

用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