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

前端 未结 9 1220
深忆病人
深忆病人 2020-11-22 12:24

I needed to write a script to enter multi-line input to a program (psql).

After a bit of googling, I found the following syntax works:

c         


        
相关标签:
9条回答
  • 2020-11-22 12:54

    Using tee instead of cat

    Not exactly as an answer to the original question, but I wanted to share this anyway: I had the need to create a config file in a directory that required root rights.

    The following does not work for that case:

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

    because the redirection is handled outside of the sudo context.

    I ended up using this instead:

    $ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
    # my config file
    foo=bar
    EOF
    
    0 讨论(0)
  • 2020-11-22 13:01

    The cat <<EOF syntax is very useful when working with multi-line text in Bash, eg. when assigning multi-line string to a shell variable, file or a pipe.

    Examples of cat <<EOF syntax usage in Bash:

    1. Assign multi-line string to a shell variable

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

    The $sql variable now holds the new-line characters too. You can verify with echo -e "$sql".

    2. Pass multi-line string to a file in Bash

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

    The print.sh file now contains:

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

    3. Pass multi-line string to a pipe in Bash

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

    The b.txt file contains bar and baz lines. The same output is printed to stdout.

    0 讨论(0)
  • 2020-11-22 13:01

    This isn't necessarily an answer to the original question, but a sharing of some results from my own testing. This:

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

    will produce the same file as:

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

    So, I don't see the point of using the cat command.

    0 讨论(0)
  • 2020-11-22 13:06

    This is called heredoc format to provide a string into stdin. See https://en.wikipedia.org/wiki/Here_document#Unix_shells for more details.


    From 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.

    0 讨论(0)
  • 2020-11-22 13:11

    POSIX 7

    kennytm quoted man bash, but most of that is also 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:

    [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 <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.

    Examples

    Some examples not yet given.

    Quotes prevent parameter expansion

    Without quotes:

    a=0
    cat <<EOF
    $a
    EOF
    

    Output:

    0
    

    With quotes:

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

    or (ugly but valid):

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

    Outputs:

    $a
    

    Hyphen removes leading tabs

    Without hyphen:

    cat <<EOF
    <tab>a
    EOF
    

    where <tab> is a literal tab, and can be inserted with Ctrl + V <tab>

    Output:

    <tab>a
    

    With hyphen:

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

    Output:

    a
    

    This exists of course so that you can indent your cat like the surrounding code, which is easier to read and maintain. E.g.:

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

    Unfortunately, this does not work for space characters: POSIX favored tab indentation here. Yikes.

    0 讨论(0)
  • 2020-11-22 13:14

    Worth noting that here docs work in bash loops too. This example shows how-to get the column list of table:

    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 ,
    

    or even without the new line

    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
    
    0 讨论(0)
提交回复
热议问题