如何在Bash脚本中将heredoc写入文件?

How can I write a heredoc to a file in Bash script?

如何将here文档写入bash脚本中的文件?


阅读高级bash脚本指南第19章。这里有文件。

下面是一个将内容写入/tmp/yourfilehere的文件的示例

1
2
3
4
cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF

注意,最后的"eof"(LimitString不应该在单词前面有任何空格,因为这意味着LimitString将不被识别。

在shell脚本中,您可能希望使用缩进使代码可读,但是这可能会产生不希望的效果,即在此处文档中缩进文本。在这种情况下,使用<<-(后跟一个破折号)禁用前导制表符(注意,要测试这一点,您需要用制表符替换前导空格,因为我无法在此处打印实际制表符。)

1
2
3
4
5
6
7
#!/usr/bin/env bash

if true ; then
    cat <<- EOF > /tmp/yourfilehere
    The leading tab is ignored.
    EOF
fi

如果不想解释文本中的变量,请使用单引号:

1
2
3
cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

要通过命令管道传送HereDoc,请执行以下操作:

1
2
3
4
5
cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

输出:

1
2
3
foo
bbr
bbz

…或者使用sudo将本文档写入文件:

1
2
3
4
5
cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF


不使用cat和I/O重定向,而是使用tee来代替:

1
2
3
4
5
tee newfile <<EOF
line 1
line 2
line 3
EOF

它更简洁,而且不像重定向操作符,如果需要写入具有根权限的文件,它可以与sudo结合使用。


注:

  • 以下是这条线索中其他答案的浓缩和整理,特别是斯特凡·拉西夫斯基和谢尔盖·斯特罗本特的优秀作品。
  • lasiewski和我在高级bash脚本指南中推荐ch 19(这里是文档)。

问题(如何在bash脚本中将here文档(又称heredoc)写入文件?)具有(至少)3个主要独立维度或子问题:

  • 要覆盖现有文件、附加到现有文件或写入新文件吗?
  • 您的用户或其他用户(如root拥有该文件吗?
  • 您是要按字面意思编写HereDoc的内容,还是让bash解释HereDoc中的变量引用?
  • (还有其他我认为不重要的维度/子问题。考虑编辑此答案以添加它们!)下面是上面列出的问题维度的一些更重要的组合,带有各种不同的分隔标识符——EOF没有什么神圣的,只需确保用作分隔标识符的字符串不会出现在Heredoc中:

  • 要覆盖您拥有的现有文件(或写入新文件),请替换HereDoc中的变量引用:

    1
    2
    3
    4
    cat << EOF > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    EOF
  • 要附加您拥有的现有文件(或写入新文件),请替换HereDoc中的变量引用:

    1
    2
    3
    4
    cat << FOE >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    FOE
  • 要使用HereDoc的文字内容覆盖您拥有的现有文件(或写入新文件):

    1
    2
    3
    4
    cat << 'END_OF_FILE' > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    END_OF_FILE
  • 要附加您拥有的现有文件(或写入新文件),请使用HereDoc的文字内容:

    1
    2
    3
    4
    cat << 'eof' >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    eof
  • 要覆盖根目录拥有的现有文件(或写入新文件),请替换HereDoc中的变量引用:

    1
    2
    3
    4
    cat << until_it_ends | sudo tee /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    until_it_ends
  • 要附加一个用户=foo拥有的现有文件(或写入新文件),以及heredoc的文本内容:

    1
    2
    3
    4
    cat << 'Screw_you_Foo' | sudo -u foo tee -a /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    Screw_you_Foo

  • 基于@livven的答案,这里有一些有用的组合。

  • 变量替换,保留前导制表符,覆盖文件,回显到stdout

    1
    2
    3
    tee /path/to/file <<EOF
    ${variable}
    EOF
  • 无变量替换,保留前导制表符,覆盖文件,回显到stdout

    1
    2
    3
    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  • 变量替换,删除前导制表符,覆盖文件,回显到stdout

    1
    2
    3
    tee /path/to/file <<-EOF
        ${variable}
    EOF
  • 变量替换,保留前导制表符,追加到文件,回显到stdout

    1
    2
    3
    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  • 变量替换,保留前导制表符,覆盖文件,不回显到stdout

    1
    2
    3
    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  • 以上也可与sudo结合使用。

    1
    2
    3
    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF

  • 当需要根权限时

    当目标文件需要根权限时,使用|sudo tee而不是>

    1
    2
    3
    cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
    The variable $FOO will *not* be interpreted.
    EOF


    对于可能有此问题的未来人员,请使用以下格式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (cat <<- _EOF_
            LogFile /var/log/clamd.log
            LogTime yes
            DatabaseDirectory /var/lib/clamav
            LocalSocket /tmp/clamd.socket
            TCPAddr 127.0.0.1
            SelfCheck 1020
            ScanPDF yes
            _EOF_
    ) > /etc/clamd.conf


    例如,您可以使用它:

    首先(进行ssh连接):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    while read pass port user ip files directs; do
        sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directs
    done <<____HERE
        PASS    PORT    USER    IP    FILES    DIRECTS
          .      .       .       .      .         .
          .      .       .       .      .         .
          .      .       .       .      .         .
        PASS    PORT    USER    IP    FILES    DIRECTS
    ____HERE

    第二个(执行命令):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    while read pass port user ip; do
        sshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1
        COMMAND 1
        .
        .
        .
        COMMAND n
    ENDSSH1

    done <<____HERE
        PASS    PORT    USER    IP
          .      .       .       .
          .      .       .       .
          .      .       .       .
        PASS    PORT    USER    IP    
    ____HERE

    第三(执行命令):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Script=$'
    #Your commands
    '


    while read pass port user ip; do
        sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip"$Script"

    done <<___HERE
    PASS    PORT    USER    IP
      .      .       .       .
      .      .       .       .
      .      .       .       .
    PASS    PORT    USER    IP  
    ___HERE

    四(使用变量):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    while read pass port user ip fileoutput; do
        sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1
        #Your command > $fileinput
        #Your command > $fileinput
    ENDSSH1

    done <<____HERE
        PASS    PORT    USER    IP      FILE-OUTPUT
          .      .       .       .          .
          .      .       .       .          .
          .      .       .       .          .
        PASS    PORT    USER    IP      FILE-OUTPUT
    ____HERE

    我喜欢这种缩进式脚本的简洁性、可读性和表示方式:

    1
    2
    3
    <<-End_of_file >file
    →       foo bar
    End_of_file

    其中,→       tabkbbkbd字符。