在bash heredoc中使用变量

问题描述 投票:183回答:3

我正在尝试在bash heredoc内插变量:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

这不符合我的预期($var实际上是被处理的,未扩展)。

我需要使用sudo tee,因为创建文件需要sudo。做类似的事情:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

无效,因为>outfile在当前外壳程序中打开文件,该外壳程序未使用sudo。

bash variables sh heredoc
3个回答
241
投票

为回答第一个问题,没有参数替换,因为您已将定界符放在引号中-the bash manual says

这里文档的格式是:

      <<[-]word
              here-document
      delimiter

无参数扩展,命令替换,算术扩展或路径名扩展是在word上执行的。如果word中的任何字符是用引号引起来的delimiter是对单词的引号删除的结果,而本文档中的行未展开。如果word未引用,则全部本文档的各行进行参数扩展,命令替换和算术扩展。 [...]

如果您将第一个示例更改为使用<<EOF而不是<< "EOF",则会发现它是有效的。

在第二个示例中,shell仅使用参数sudo调用cat,并且重定向以原始用户身份应用于sudo cat的输出。如果您尝试,它将起作用:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT

93
投票

不要在<<EOF中使用引号:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

可变扩展是here-docs内部的默认行为。您可以通过引用标签(单引号或双引号)来禁用该行为。


30
投票

作为此处较早答案的后推论,您可能最终遇到了想要插入[[some但不插入all变量的情况。您可以通过使用反斜杠来避免美元符号和反引号来解决此问题;或者您可以将静态文本放入变量中。

Name='Rich Ba$tard' dough='$$$dollars$$$' cat <<____HERE $Name, you can win a lot of $dough this week! Notice that \`backticks' need escaping if you want literal text, not `pwd`, just like in variables like \$HOME (current value: $HOME) ____HERE
演示:https://ideone.com/rMF2XA

请注意,任何引用机制-\____HERE"____HERE"'____HERE'-都将禁用所有变量插值,并将此处的文档转换为一段文字。

一项常见的任务是将局部变量与脚本结合在一起,应该由不同的外壳程序,编程语言或远程主机进行评估。

local=$(uname) ssh -t remote <<: echo "$local is the value from the host which ran the ssh command" # Prevent here doc from expanding locally; remote won't see backslash remote=\$(uname) # Same here echo "\$remote is the value from the host we ssh:ed to" :


0
投票

使用简单的Python脚本

这些天,大多数系统都准备使用python。因此,这是一个适用于ya的简单脚本:

# replace.py
# USAGE: python replace.py bad-word good-word target-file.txt
#
import sys

search_term = sys.argv[1]
replace_term = sys.argv[2]
target_file = sys.argv[3]

with open(target_file, 'r') as file:
        content = file.read()

content = content.replace(search_term, replace_term)

with open(target_file, 'w') as file:
        file.write(content)

一个警告:如果您的好/坏词已经在sh系统/环境变量中,则此方法非常有用。只要确保在传递给脚本时使用双引号将变量包装即可。

例如:

python replace.py "$BAD_WORD" "$GOOD_WORD" target-file.txt

但是,这些将不起作用

# This breaks on $ or " characters
BAD_WORD="your-artibrary-string"

# This breaks on ' characters
BAD_WORD='your-artibrary-string'

# This breaks on spaces plus a variety of characters
BAD_WORD=your-artibrary-string

处理任意文字字符

1。将字符写入磁盘

如果需要向脚本提供任意文字值(跳过任何转义符,通常使用此方法将其写入磁盘:

head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
arbitrary-one-line-string
CRAZY_LONG_EOF_MARKER

...其中:

  • 我们正在使用Here Document机制来编写文字文本
  • 我们正在使用headtee删除Here Docs创建的尾随换行符
  • 我们通过引用EOL标记字符串来防止对Here Doc中的变量进行评估

这里是一个带有棘手字符的快速演示:

head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
1"2<3>4&5'6$7 # 8
CRAZY_LONG_EOF_MARKER

2。使用修改后的Python脚本

这是从Word文件读取的更新脚本:

# replace.py
# USAGE: python replace.py bad-word.txt good-word.txt target-file.txt
#
import sys

search_term_file = sys.argv[1]
replace_term_file = sys.argv[2]
target_file = sys.argv[3]

print [search_term_file, replace_term_file, target_file]

with open(search_term_file, 'r') as file:
        search_term = file.read()
with open(replace_term_file, 'r') as file:
        replace_term = file.read()
with open(target_file, 'r') as file:
        content = file.read()

print [search_term, replace_term]
content = content.replace(search_term, replace_term)

with open(target_file, 'w') as file:
        file.write(content)
© www.soinside.com 2019 - 2024. All rights reserved.