我怎么能写一个heldoc到一个文件在Bash脚本?

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

532610 次浏览

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

(cat <<- _EOF_LogFile /var/log/clamd.logLogTime yesDatabaseDirectory /var/lib/clamavLocalSocket /tmp/clamd.socketTCPAddr 127.0.0.1SelfCheck 1020ScanPDF yes_EOF_) > /etc/clamd.conf

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

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

cat << EOF > /tmp/yourfilehereThese contents will be written to the file.This line is indented.EOF

请注意,最后的“EOF”(LimitString)不应在单词前面有任何空格,因为这意味着LimitString将无法识别。

在外壳脚本中,您可能希望使用缩进来使代码可读,但是这可能会产生缩进这里文档中文本的不良效果。在这种情况下,使用<<-(后跟破折号)来禁用前导制表符(说明,为了测试这一点,您需要用制表符替换前导空格,因为我不能在这里打印实际的制表符。)

#!/usr/bin/env bash
if true ; thencat <<- EOF > /tmp/yourfilehereThe leading tab is ignored.EOFfi

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

cat << 'EOF' > /tmp/yourfilehereThe variable $FOO will not be interpreted.EOF

要通过命令管道管道处理heldoc:

cat <<'EOF' |  sed 's/a/b/'foobarbazEOF

输出:

foobbrbbz

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

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conffoobarbazEOF

与其使用cat和I/O重定向,不如使用tee

tee newfile <<EOFline 1line 2line 3EOF

它更简洁,而且与重定向运算符不同,如果您需要写入具有root权限的文件,它可以与sudo结合使用。

当需要root权限时

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

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehereThe variable $FOO will *not* be interpreted.EOF
cat << "EOF" |sudo tee /tmp/yourprotectedfilehereThe variable $FOO *will* be interpreted.EOF

例如,您可以使用它:

首先(建立ssh连接):

while read pass port user ip files directs; dosshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directsdone <<____HEREPASS    PORT    USER    IP    FILES    DIRECTS.      .       .       .      .         ..      .       .       .      .         ..      .       .       .      .         .PASS    PORT    USER    IP    FILES    DIRECTS____HERE

第二(执行命令):

while read pass port user ip; dosshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1COMMAND 1...COMMAND nENDSSH1done <<____HEREPASS    PORT    USER    IP.      .       .       ..      .       .       ..      .       .       .PASS    PORT    USER    IP____HERE

第三(执行命令):

Script=$'#Your commands'
while read pass port user ip; dosshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip "$Script"
done <<___HEREPASS    PORT    USER    IP.      .       .       ..      .       .       ..      .       .       .PASS    PORT    USER    IP___HERE

Forth(使用变量):

while read pass port user ip fileoutput; dosshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1#Your command > $fileinput#Your command > $fileinputENDSSH1done <<____HEREPASS    PORT    USER    IP      FILE-OUTPUT.      .       .       .          ..      .       .       .          ..      .       .       .          .PASS    PORT    USER    IP      FILE-OUTPUT____HERE

备注:

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

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

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

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

    cat << EOF > /path/to/your/fileThis line will write to the file.${THIS} will also write to the file, with the variable contents substituted.EOF
  2. To append an existing file (or write to a new file) that you own, substituting variable references inside the heredoc:

    cat << FOE >> /path/to/your/fileThis line will write to the file.${THIS} will also write to the file, with the variable contents substituted.FOE
  3. To overwrite an existing file (or write to a new file) that you own, with the literal contents of the heredoc:

    cat << 'END_OF_FILE' > /path/to/your/fileThis line will write to the file.${THIS} will also write to the file, without the variable contents substituted.END_OF_FILE
  4. To append an existing file (or write to a new file) that you own, with the literal contents of the heredoc:

    cat << 'eof' >> /path/to/your/fileThis line will write to the file.${THIS} will also write to the file, without the variable contents substituted.eof
  5. To overwrite an existing file (or write to a new file) owned by root, substituting variable references inside the heredoc:

    cat << until_it_ends | sudo tee /path/to/your/fileThis line will write to the file.${THIS} will also write to the file, with the variable contents substituted.until_it_ends
  6. To append an existing file (or write to a new file) owned by user=foo, with the literal contents of the heredoc:

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

为了建立@Livven的回答,这里有一些有用的组合。

  1. 变量替换,保留前导制表符,覆盖文件,回显到标准输出

    tee /path/to/file <<EOF${variable}EOF
  2. no variable substitution, leading tab retained, overwrite file, echo to stdout

    tee /path/to/file <<'EOF'${variable}EOF
  3. variable substitution, leading tab removed, overwrite file, echo to stdout

    tee /path/to/file <<-EOF${variable}EOF
  4. variable substitution, leading tab retained, append to file, echo to stdout

    tee -a /path/to/file <<EOF${variable}EOF
  5. variable substitution, leading tab retained, overwrite file, no echo to stdout

    tee /path/to/file <<EOF >/dev/null${variable}EOF
  6. the above can be combined with sudo as well

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

我喜欢这种简洁、易读性和缩进脚本演示的方法:

<<-End_of_file >file→       foo barEnd_of_file

其中→       选项卡字符。

如果你想保持heldoc缩进的易读性:

$ perl -pe 's/^\s*//' << EOFline 1line 2EOF

在Bash中支持缩进heldoc的内置方法只支持前导制表符,不支持空格。

Perl可以用awk替换以保存几个字符,但如果您了解基本的正则表达式,Perl可能更容易记住。

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

# here-doc tab indented{ read -r -d '' || printf >file '%s' "$REPLY"; } <<-EOFfoo barEOF

或者对于一个简单的“mycat”函数(并避免在环境中留下REPLY):

mycat() {local REPLYread -r -d '' || printf '%s' "$REPLY"}mycat >file <<-EOFfoo barEOF

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

mycat:real    0m1.507suser    0m0.108ssys     0m0.488s
OS cat:real    0m4.082suser    0m0.716ssys     0m1.808s

注意:mycat不处理文件参数,它只是处理问题“将heldoc写入文件”

此外,如果您正在写入文件,检查您的写入是否成功或失败可能是一个好主意。例如:

if ! echo "contents" > ./file ; thenecho "ERROR: failed to write to file" >& 2exit 1fi

要对heldoc做同样的事情,有两种可能的方法。

1)

if ! cat > ./file << EOFcontentsEOFthenecho "ERROR: failed to write to file" >& 2exit 1fi
if ! cat > ./file ; thenecho "ERROR: failed to write to file" >& 2exit 1fi << EOFcontentsEOF

您可以通过将目标文件./file替换为/file来测试上述代码中的错误情况(假设您不是以root身份运行)。