我需要编写一个脚本来向程序 (psql
) 输入多行输入。
经过一番谷歌搜索,我发现以下语法有效:
cat << EOF | psql ---params
BEGIN;
`pg_dump ----something`
update table .... statement ...;
END;
EOF
这会正确构造多行字符串(从 BEGIN;
到 END;
,包括在内)并将其作为输入传递给 psql
。
但我不知道它是如何/为什么起作用的,有人可以解释一下吗?
我主要指的是 cat << EOF
,我知道 >
输出到文件,>>
附加到文件,<
从文件中读取输入。
<<
究竟做了什么?
是否有它的手册页?
cat
的无用使用。试试 psql ... << EOF ...
另见“这里的字符串”。 mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
cat
读取 stding(当通过管道传输到它时)或读取与其命令行参数对应的文件
cat <<EOF
语法在 Bash 中处理多行文本时非常有用,例如。将多行字符串分配给 shell 变量、文件或管道时。
Bash 中 cat <
1.将多行字符串分配给shell变量
2. 将多行字符串传递给 Bash 中的文件
3. 将多行字符串传递给 Bash 中的管道
$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)
$sql
变量现在也包含换行符。您可以通过 echo -e "$sql"
进行验证。
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
print.sh
文件现在包含:
#!/bin/bash
echo $PWD
echo /home/user
$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF
b.txt
文件包含 bar
和 baz
行。将相同的输出打印到 stdout
。
这称为 heredoc 格式 以将字符串提供到标准输入中。有关详细信息,请参阅 https://en.wikipedia.org/wiki/Here_document#Unix_shells。
从 man bash
:
Here Documents 这种类型的重定向指示 shell 从当前源读取输入,直到看到只包含单词(没有尾随空格)的行。然后将所有读取到该点的行用作命令的标准输入。 here-documents 的格式为: <<[-]word here-document delimiter 不对word 进行参数扩展、命令替换、算术扩展或路径名扩展。如果 word 中的任何字符被引用,则分隔符是 word 上去除引号的结果,并且 here-document 中的行不展开。如果 word 没有被引用,则 here-document 的所有行都经过参数扩展、命令替换和算术扩展。在后一种情况下,字符序列 \
<<-
,请注意只有前导 制表符 字符会被删除,而不是软制表符。这是您真正需要制表符的罕见情况之一。如果文档的其余部分使用软制表符,请确保显示不可见字符并(例如)复制和粘贴制表符。如果你做对了,你的语法高亮应该正确地捕捉到结束分隔符。
在您的情况下,“EOF”被称为“Here Tag”。基本上 <<Here
告诉 shell 你要输入一个多行字符串,直到“标签”Here
。您可以随意命名此标签,通常是 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:
重定向运算符 "<<" 和 "<<-" 都允许将 shell 输入文件(称为“here-document”)中包含的行重定向到命令的输入。 here-document 应被视为在下一个
例子
一些例子还没有给出。
引号防止参数扩展
不带引号:
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
缩进。哎呀。
<<-
和 <tab>a
的最后一个示例中,应该注意的是,其目的是允许脚本中的代码正常缩进,同时允许呈现给接收进程的 heredoc 文本从第 0 列开始。这不是太常见的功能和更多的上下文可能会阻止很多令人头疼的事情......
$
前面使用反斜杠
"$a"'$b'"$c"
之类的引号,但这里没有类似的 AFAIK。
使用 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
对上述答案的一点扩展。结尾的 >
将输入定向到文件中,覆盖现有内容。但是,一种特别方便的用法是附加的双箭头 >>
,将新内容添加到文件末尾,如下所示:
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
,您不必担心意外修改其任何内容。
<< EoF
基本上意味着:
<<
- “读取从下一行开始的多行输入,并将其视为单独文件中的代码”
EoF
- “在多行输入中找到单词 EoF
后立即停止阅读”
正如其他答案所解释的,多行输入称为 Here Document
Here Document 通常用于生成要传递给后续流程的输出。例如,cat << EoF
可用于使用 Here Document 生成所需的输出。
下面是使用 Here Document 动态创建文本文档的示例:
cat << EoF > ./my-document.txt
Hello world
Have a nice day
EoF
长话短说,EOF
标记(但也可以使用不同的文字)是一种heredoc 格式,允许您以多行的形式提供输入。很多困惑来自 cat
的实际工作方式。您可以将 cat
与 >>
或 >
一起使用,如下所示:
$ cat >> temp.txt
line 1
line 2
虽然可以在手动写入控制台时以这种方式使用 cat
,但如果我想以更具声明性的方式提供输入以便它可以被工具重用并保留缩进、空格等,这并不方便。
Heredoc 允许定义您的整个输入,就像您不使用 stdin
而是在单独的文本编辑器中输入一样。这就是 Wikipedia 文章的含义:
它是源代码文件的一部分,被视为一个单独的文件。
这不一定是对原始问题的回答,而是分享我自己测试的一些结果。这个:
<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
将产生与以下相同的文件:
cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
所以,我看不出使用 cat 命令的意义。
cat <<test
的 <<test
时,两者都创建了一个空文件。
请注意, 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"
创建 json 文件的示例:
cat << EoF > ./allaccess.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::*"
]
}
]
}
EoF
因此:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::*"
]
}
]
}
值得注意的是,这里的文档也可以在 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
不定期副业成功案例分享
tee
而不是cat
时,可以使用sudo
写入受限位置的文件。比如sudo tee /etc/somepath/file > /dev/null <<EOF ...
cat
仅适用于带有here-document的完整bash命令,如果没有cat
或其他命令,则以符号<<
开头的here-document无法回显到stdout
,其余的第一个行“grep 'b' | tee b.txt”无法获得输入。