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

Jos*_*eld 697 bash heredoc

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

Ste*_*ski 983

阅读Advanced Bash-Scripting Guide 第19章.这里的文档.

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

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF
Run Code Online (Sandbox Code Playgroud)

请注意,最后的'EOF'(The LimitString)不应该在单词前面有任何空格,因为这意味着LimitString不会被识别.

在shell脚本中,您可能希望使用缩进来使代码可读,但是这会使您在此文档中缩进文本产生不良影响.在这种情况下,使用<<-(后跟破折号)禁用前导选项卡(请注意,为了测试它,您需要使用制表符替换前导空格,因为我无法在此处打印实际的制表符.)

#!/usr/bin/env bash

if true ; then
    cat <<- EOF > /tmp/yourfilehere
    The leading tab is ignored.
    EOF
fi
Run Code Online (Sandbox Code Playgroud)

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

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF
Run Code Online (Sandbox Code Playgroud)

通过命令管道管道heredoc:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF
Run Code Online (Sandbox Code Playgroud)

输出:

foo
bbr
bbz
Run Code Online (Sandbox Code Playgroud)

...或者使用sudo以下方法将heredoc写入文件:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
Run Code Online (Sandbox Code Playgroud)

  • @PineappleUndertheSea` <<<`被称为'Here Strings'.像`tr az AZ <<<'one two three'`这样的代码将导致字符串'ONE TWO THREE`.有关更多信息,请访问http://en.wikipedia.org/wiki/Here_document#Here_strings (17认同)
  • 你甚至不需要Bash,这个功能也在Bourne/Korn/POSIX shell中. (11认同)
  • 最终的EOF在*之后也不应该有任何空格*.至少在bash上,这导致它被无法识别为分隔符 (7认同)
  • 由于这个特殊的heredoc意图是文字内容,而不是包含替换,它应该是"<< EOF"而不是"<< EOF". (7认同)
  • 怎么样`<<<`,他们叫什么? (5认同)
  • @StefanLasiewski抱歉,我不是在建议您的示例不起作用。您说过“最后的'EOF'字词前不应有任何空格” –我在OSX 10.9和Bash上发现的是,EOF之后的尾部空格也可能导致其失败。我只是在发表评论以帮助其他人,因为我花了一段时间才弄清楚:) (2认同)
  • 您可以添加“ EOF”不是特殊关键字,而可以使用任何其他限制词代替。在您的示例中,“ EOF”是一个元语法变量。 (2认同)

Liv*_*ven 142

而不是使用cat和I/O重定向,而是使用它可能是有用的tee:

tee newfile <<EOF
line 1
line 2
line 3
EOF
Run Code Online (Sandbox Code Playgroud)

它更简洁,加上与重定向操作符不同,sudo如果您需要使用root权限写入文件,它可以与之结合使用.

  • 我建议在第一行的末尾添加`>/dev/null`,以防止在创建时将此文件的内容显示为stdout. (18认同)
  • 没错,但是你的解决方案因为它与`sudo`的兼容性而吸引我,而不是因为它简洁:-) (9认同)
  • @MountainX查看`man tee`.使用`-a`标志来追加而不是覆盖. (5认同)
  • 为了在有时需要监视的配置脚本中使用,我更喜欢这一点,因为它可以打印内容。 (3认同)
  • 您将如何使用此方法附加到现有文件? (2认同)

Tom*_*che 55

注意:

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

  1. 是要覆盖现有文件,附加到现有文件还是写入新文件?
  2. 您的用户或其他用户(例如root)是否拥有该文件?
  3. 你想在字面上写下你的heredoc的内容,还是要在你的heredoc里面用bash解释变量引用?

(还有其他维度/子问题我认为不重要.请考虑编辑此答案以添加它们!)以下是上面列出的问题维度的一些更重要的组合,具有各种不同的分隔标识符 - 没有什么神圣的EOF,只要确保你的heredoc 中没有出现用作分界标识符的字符串:

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

    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
    
    Run Code Online (Sandbox Code Playgroud)
  2. 要附加您拥有的现有文件(或写入新文件),请替换heredoc中的变量引用:

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 要使用heredoc的文字内容覆盖您拥有的现有文件(或写入新文件):

    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
    
    Run Code Online (Sandbox Code Playgroud)
  4. 要附加您拥有的现有文件(或写入新文件),请使用heredoc的文字内容:

    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
    
    Run Code Online (Sandbox Code Playgroud)
  5. 要覆盖root拥有的现有文件(或写入新文件),请替换heredoc中的变量引用:

    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
    
    Run Code Online (Sandbox Code Playgroud)
  6. 要附加user = foo拥有的现有文件(或写入新文件),并使用heredoc的文字内容:

    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
    
    Run Code Online (Sandbox Code Playgroud)

  • @Aleksandr Makov:如何使用#6覆盖现有文件的内容?省略`-a` ==`--append`; 即`tee -a`-&gt;`tee`。参见`info tee`(我在这里引用了它,但是注释标记太有限了。 (2认同)
  • @becko:只是为了说明标签只是标签。请注意,在每个示例中我都使用了不同的标签。 (2认同)

go2*_*ull 35

基于@Livven的答案,这里有一些有用的组合.

  1. 变量替换,保留前导选项卡,覆盖文件,回显到标准输出

    tee /path/to/file <<EOF
    ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)
  2. 没有变量替换,保留选项卡保留,覆盖文件,回显到stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)
  3. 变量替换,删除前导标签,覆盖文件,回显到标准输出

    tee /path/to/file <<-EOF
        ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)
  4. 变量替换,保留前导选项卡,附加到文件,回显到stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)
  5. 变量替换,保留前导选项卡,覆盖文件,没有回显到stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)
  6. 以上可以结合sudo以及

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF
    
    Run Code Online (Sandbox Code Playgroud)


Ser*_*ndt 16

需要root权限时

如果目标文件需要root权限,请使用|sudo tee而不是>:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF
Run Code Online (Sandbox Code Playgroud)

  • @ user1527227只是不要将EOF用单引号引起来。然后,将解释$ FOO。 (3认同)

Jos*_*eld 11

对于可能遇到此问题的未来人员,以下格式有效:

(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
Run Code Online (Sandbox Code Playgroud)

  • 不需要括号:`cat << END> afile`后跟heredoc的效果非常好. (6认同)
  • 这不行.输出重定向需要位于以"cat"开头的行的末尾,如接受的答案中所示. (2认同)
  • @DennisWilliamson这是有效的,这就是parens的用途.整个`cat`在子shell中运行,子shell的所有输出都被重定向到文件 (2认同)
  • @Izkata:如果您查看此答案的编辑历史记录,会在我发表评论之前删除括号,然后再添加回来。格伦杰克曼(和我)的评论适用。 (2认同)

Sco*_*ger 6

对于那些寻找纯 bash 解决方案(或需要速度)的人来说,这是一个不带 cat 的简单解决方案:

# here-doc tab indented
{ read -r -d '' || printf >file '%s' "$REPLY"; } <<-EOF
        foo bar
EOF
Run Code Online (Sandbox Code Playgroud)

或用于简单的“mycat”功能(并避免将 REPLY 留在环境中):

mycat() {
  local REPLY
  read -r -d '' || printf '%s' "$REPLY"
}
mycat >file <<-EOF
        foo bar
EOF
Run Code Online (Sandbox Code Playgroud)

“mycat”与 OS cat 的快速速度比较(在我的 OSX 笔记本电脑上循环 >/dev/null 1000 次):

mycat:
real    0m1.507s
user    0m0.108s
sys     0m0.488s

OS cat:
real    0m4.082s
user    0m0.716s
sys     0m1.808s
Run Code Online (Sandbox Code Playgroud)

注意:mycat 不处理文件参数,它只处理“将定界符写入文件”的问题