关于linux:“cat<<

How does “cat << EOF” work in bash?

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

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

1
2
3
4
5
6
7
8
9
cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF

这正确地构造了多行字符串(从BEGIN;END;,包括这两个字符串),并将其作为输入传输到psql

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

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

<<到底做了什么?

有没有它的主页?


这被称为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:

1
2
3
          <<[-]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 \ 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中的多行文本时(例如,将多行字符串分配给shell变量、文件或管道时),cat <语法非常有用。

bash中cat <语法用法示例:1。将多行字符串赋给shell变量

1
2
3
4
5
$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF

)

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

2。将多行字符串传递到bash中的文件

1
2
3
4
5
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

print.sh文件现在包含:

1
2
3
#!/bin/bash
echo $PWD
echo /home/user

三。将多行字符串传递到bash中的管道

1
2
3
4
5
$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

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


在您的例子中,"eof"被称为"here tag"。基本上,<告诉shell您将输入一个多行字符串,直到"tag"Here。您可以根据需要命名这个标签,它通常是EOFSTOP

关于此处标签的一些规则:

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

    1
    2
    3
    4
    5
    6
    $ 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


    位置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 and continues until there is a line containing only the delimiter and a , with no characters in between. Then the next here-document starts, if there is one. The format is as follows:

    1
    2
    3
    [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 in the input behaves as the 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 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.

    号示例

    还没有给出一些例子。

    引号防止参数扩展

    不带引号:

    1
    2
    3
    4
    a=0
    cat <<EOF
    $a
    EOF

    输出:

    1
    0

    带引号:

    1
    2
    3
    4
    a=0
    cat <<'EOF'
    $a
    EOF

    或(丑陋但有效):

    1
    2
    3
    4
    a=0
    cat <<E"O"F
    $a
    EOF

    输出:

    1
    $a

    连字符删除前导制表符

    不带连字符:

    1
    2
    3
    cat <<EOF
    <tab>a
    EOF

    其中,是文字标签,可与Ctrl + V 一起插入。

    输出:

    1
    <tab>a

    带连字符:

    1
    2
    3
    cat <<-EOF
    <tab>a
    <tab>EOF

    输出:

    1
    a

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

    1
    2
    3
    4
    5
    if true; then
        cat <<-EOF
        a
        EOF
    fi

    不幸的是,这不适用于空格字符:posix支持在这里使用tab缩进。伊克斯。


    用T恤代替猫

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

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

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

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

    我最终用这个来代替:

    1
    2
    3
    4
    $ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
    # my config file
    foo=bar
    EOF


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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 ,

    甚至没有新线

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    while read -r c; do test -z"$c" || echo $table_name.$c , | perl -ne
    's/
    //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


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

    1
    2
    3
    4
    5
    <<test > print.sh
    #!/bin/bash
    echo \$PWD
    echo $PWD
    test

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

    1
    2
    3
    4
    5
    cat <<test > print.sh
    #!/bin/bash
    echo \$PWD
    echo $PWD
    test

    所以,我不明白使用cat命令的意义。