【发布时间】:2011-02-26 12:58:41
【问题描述】:
如何在 Bash 脚本中将 here 文档写入文件?
【问题讨论】:
如何在 Bash 脚本中将 here 文档写入文件?
【问题讨论】:
阅读高级 Bash 脚本指南Chapter 19. Here Documents。
这是一个将内容写入/tmp/yourfilehere的文件的示例
cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
This line is indented.
EOF
请注意,最后的 'EOF' (LimitString) 在单词前面不应有任何空格,因为这意味着将无法识别 LimitString。
在 shell 脚本中,您可能希望使用缩进来使代码可读,但是这可能会产生缩进 here 文档中的文本的不良影响。在这种情况下,请使用 <<-(后跟破折号)禁用前导标签(注意,要对此进行测试,您需要将前导空格替换为制表符 ,因为我无法在此处打印实际的制表符。)
#!/usr/bin/env bash
if true ; then
cat <<- EOF > /tmp/yourfilehere
The leading tab is ignored.
EOF
fi
如果你不想解释文本中的变量,那么使用单引号:
cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF
通过命令管道传递heredoc:
cat <<'EOF' | sed 's/a/b/'
foo
bar
baz
EOF
输出:
foo
bbr
bbz
... 或使用sudo 将heredoc 写入文件:
cat <<'EOF' | sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
【讨论】:
<<< 呢,他们叫什么?
<<< 被称为“这里的字符串”。像tr a-z A-Z <<< 'one two three' 这样的代码将产生字符串ONE TWO THREE。更多信息en.wikipedia.org/wiki/Here_document#Here_strings
<<'EOF' 而不是<<EOF。
使用 tee 代替 cat 和 I/O 重定向可能会有用:
tee newfile <<EOF
line 1
line 2
line 3
EOF
它更简洁,而且与重定向运算符不同,如果您需要写入具有 root 权限的文件,它可以与 sudo 结合使用。
【讨论】:
> /dev/null,以防止此处文件的内容在创建时显示到标准输出。
sudo 兼容,而不是因为它的简洁性:-)
man tee。使用-a 标志追加而不是覆盖。
注意:
问题(如何将 here 文档(又名 heredoc)写入 bash 脚本中的文件?)具有(至少)3 个主要的独立维度或子问题:
root)是否拥有该文件?(还有其他我认为不重要的维度/子问题。考虑编辑此答案以添加它们!)以下是上面列出的问题维度的一些更重要的组合,以及各种不同的定界标识符 - -EOF 没有什么神圣之处,只要确保您用作分隔标识符的字符串不出现在您的 heredoc 中:
覆盖您拥有的现有文件(或写入新文件),替换 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
要附加您拥有的现有文件(或写入新文件),替换 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
使用 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
使用 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
要覆盖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
使用 heredoc 的文字内容附加 user=foo 拥有的现有文件(或写入新文件):
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
【讨论】:
-a == --append;即,tee -a -> tee。见info tee(我会在这里引用它,但评论标记太有限了。
sudo tee /path/to/your/file << 'Screw_you_Foo' 是否有好处?
FOE 而不是EOF?
以@Livven 的answer 为基础,这里有一些有用的组合。
变量替换,保留前导选项卡,覆盖文件,回显到标准输出
tee /path/to/file <<EOF
${variable}
EOF
无变量替换,保留前导选项卡,覆盖文件,回显到标准输出
tee /path/to/file <<'EOF'
${variable}
EOF
变量替换,删除前导标签,覆盖文件,回显到标准输出
tee /path/to/file <<-EOF
${variable}
EOF
变量替换,保留前导标签,附加到文件,回显到标准输出
tee -a /path/to/file <<EOF
${variable}
EOF
变量替换,保留前导选项卡,覆盖文件,不回显到标准输出
tee /path/to/file <<EOF >/dev/null
${variable}
EOF
上面也可以和sudo结合
sudo -u USER tee /path/to/file <<EOF
${variable}
EOF
【讨论】:
当目标文件需要root权限时,使用|sudo tee而不是>:
cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF
cat << "EOF" |sudo tee /tmp/yourprotectedfilehere
The variable $FOO *will* be interpreted.
EOF
【讨论】:
| sudo cat > 而不是| sudo tee。当然,现在你可能使用了两次cat,并且双重调用了“不必要地使用猫”的模因。
对于未来可能遇到此问题的人,以下格式有效:
(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
【讨论】:
cat << END > afile 后跟heredoc 效果很好。
cat 开头的行的末尾,如接受的答案所示。
cat在一个子shell中运行,子shell的所有输出都被重定向到文件中
作为实例,您可以使用它:
首先(建立 ssh 连接):
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
第二(执行命令):
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
第三(执行命令):
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
Forth(使用变量):
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
【讨论】:
对于那些寻找纯 bash 解决方案(或需要速度)的人,这里有一个没有 cat 的简单解决方案:
# here-doc tab indented
{ read -r -d '' || printf >file '%s' "$REPLY"; } <<-EOF
foo bar
EOF
或简单的“mycat”功能(并避免将 REPLY 留在环境中):
mycat() {
local REPLY
read -r -d '' || printf '%s' "$REPLY"
}
mycat >file <<-EOF
foo bar
EOF
“mycat”与 OS cat 的快速速度比较(我的 OSX 笔记本电脑上 1000 个循环 >/dev/null):
mycat:
real 0m1.507s
user 0m0.108s
sys 0m0.488s
OS cat:
real 0m4.082s
user 0m0.716s
sys 0m1.808s
注意:mycat 不处理文件参数,它只是处理“将 heredoc 写入文件”的问题
【讨论】:
我喜欢这种在缩进脚本中简洁、可读性和演示的方法:
<<-End_of_file >file
→ foo bar
End_of_file
→ 是 tab 字符。
【讨论】:
如果你想让 heredoc 缩进以提高可读性:
$ perl -pe 's/^\s*//' << EOF
line 1
line 2
EOF
Bash中支持缩进heredoc的内置方法只支持前导制表符,不支持空格。
Perl 可以替换为 awk 以节省一些字符,但如果您了解基本的正则表达式,Perl 可能更容易记住。
【讨论】: