如何将此处的文档写入 Bash 脚本中的文件?
10 回答
阅读高级 Bash 脚本指南第 19 章。这里是 Documents。
这是一个将内容写入文件的示例/tmp/yourfilehere
cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
This line is indented.
EOF
请注意,最后的 'EOF' (The 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
...或使用以下命令将heredoc写入文件sudo
:
cat <<'EOF' | sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
与其使用cat
和 I/O 重定向,不如使用它可能有用tee
:
tee newfile <<EOF
line 1
line 2
line 3
EOF
sudo
它更简洁,而且与重定向运算符不同,如果您需要写入具有 root 权限的文件,它可以结合使用。
笔记:
- 以下内容浓缩并组织了该线程中的其他答案,尤其是Stefan Lasiewski和Serge Stroobandt的出色工作
- Lasiewski 和我推荐Advanced Bash-Scripting Guide 中的第 19 章(此处是文档)
问题(如何将 here 文档(又名heredoc)写入 bash 脚本中的文件?)(至少)有 3 个主要的独立维度或子问题:
- 您要覆盖现有文件、追加到现有文件还是写入新文件?
- 您的用户或其他用户(例如
root
)是否拥有该文件? - 你想直接写你的heredoc的内容,还是让bash解释你的heredoc中的变量引用?
(还有其他我认为不重要的维度/子问题。考虑编辑此答案以添加它们!)以下是上面列出的问题维度的一些更重要的组合,以及各种不同的定界标识符——没有什么神圣的 about 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
为了以@Livven 的回答为基础,这里有一些有用的组合。
变量替换,保留前导选项卡,覆盖文件,回显到标准输出
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 权限时
当目标文件需要 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
对于未来可能遇到此问题的人,以下格式有效:
(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连接):
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
第四(使用变量):
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
For those looking for a pure bash solution (or a need for speed), here's a simple solution without cat:
# here-doc tab indented
{ read -r -d '' || printf >file '%s' "$REPLY"; } <<-EOF
foo bar
EOF
or for an easy "mycat" function (and avoid leaving REPLY in environment):
mycat() {
local REPLY
read -r -d '' || printf '%s' "$REPLY"
}
mycat >file <<-EOF
foo bar
EOF
Quick speed comparison of "mycat" vs OS cat (1000 loops >/dev/null on my OSX laptop):
mycat:
real 0m1.507s
user 0m0.108s
sys 0m0.488s
OS cat:
real 0m4.082s
user 0m0.716s
sys 0m1.808s
NOTE: mycat doesn't handle file arguments, it just handles the problem "write a heredoc to a file"
If you want to keep the heredoc indented for readability:
$ perl -pe 's/^\s*//' << EOF
line 1
line 2
EOF
The built-in method for supporting indented heredoc in Bash only supports leading tabs, not spaces.
Perl can be replaced with awk to save a few characters, but the Perl one is probably easier to remember if you know basic regular expressions.