bash 如何替换文本文件中的 ${} 占位符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/415677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:53:09  来源:igfitidea点击:

How to replace ${} placeholders in a text file?

bashcommand-linetext-processingtemplating

提问by Dana the Sane

I want to pipe the output of a "template" file into MySQL, the file having variables like ${dbName}interspersed. What is the command line utility to replace these instances and dump the output to standard output?

我想将“模板”文件的输出通过管道传输到 MySQL,该文件中${dbName}散布着一些变量。替换这些实例并将输出转储到标准输出的命令行实用程序是什么?

回答by user

Sed!

塞德

Given template.txt:

给定模板.txt:

The number is ${i}
The word is ${word}

we just have to say:

我们只需要说:

sed -e "s/${i}/1/" -e "s/${word}/dog/" template.txt

Thanks to Jonathan Leffler for the tip to pass multiple -earguments to the same sedinvocation.

感谢 Jonathan Leffler 提供将多个-e参数传递给同一个sed调用的技巧。

回答by plockc

Update

更新

Here is a solution from yottatsaon a similar question that only does replacement for variables like $VAR or ${VAR}, and is a brief one-liner

这是yottatsa对类似问题的解决方案,该解决方案仅替换 $VAR 或 ${VAR} 等变量,并且是一个简短的单行

i=32 word=foo envsubst < template.txt

Of course if iand wordare in your environment, then it is just

当然,如果iword在您的环境中,那么它只是

envsubst < template.txt

On my Mac it looks like it was installed as part of gettextand from MacGPG2

在我的 Mac 上,它看起来像是作为gettextMacGPG2 的一部分安装的

Old Answer

旧答案

Here is an improvement to the solution from mogsieon a similar question, my solution does not require you to escale double quotes, mogsie's does, but his is a one liner!

这是对mogsie对类似问题的解决方案的改进,我的解决方案不需要您对双引号进行缩放,mogsie 的需要,但他的解决方案是单行的!

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

The power on these two solutions is that you only get a few types of shell expansions that don't occur normally $((...)), `...`, and $(...), though backslash isan escape character here, but you don't have to worry that the parsing has a bug, and it does multiple lines just fine.

这两种解决方案的强大之处在于,您只能获得一些通常不会出现的 shell 扩展类型 $((...))、`...` 和 $(...),尽管反斜杠一个在这里转义字符,但您不必担心解析有错误,并且它可以执行多行。

回答by gnud

Use /bin/sh. Create a small shell script that sets the variables, and then parse the template using the shell itself. Like so (edit to handle newlines correctly):

使用/bin/sh. 创建一个设置变量的小 shell 脚本,然后使用 shell 本身解析模板。像这样(编辑以正确处理换行符):

File template.txt:

文件模板.txt:

the number is ${i}
the word is ${word}

File script.sh:

文件脚本.sh:

#!/bin/sh

#Set variables
i=1
word="dog"

#Read in template one line at the time, and replace variables (more
#natural (and efficient) way, thanks to Jonathan Leffler).
while read line
do
    eval echo "$line"
done < "./template.txt"

Output:

输出:

#sh script.sh
the number is 1
the word is dog

回答by Dana the Sane

I was thinking about this again, given the recent interest, and I think that the tool that I was originally thinking of was m4, the macro processor for autotools. So instead of the variable I originally specified, you'd use:

考虑到最近的兴趣,我又在考虑这个问题,我认为我最初想到的工具是m4autotools 的宏处理器。因此,不是我最初指定的变量,而是使用:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"

回答by Dana the Sane

template.txt

模板.txt

Variable 1 value: ${var1}
Variable 2 value: ${var2}

data.sh

数据文件

#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"

parser.sh

解析器

#!/usr/bin/env bash

# args
declare file_data=
declare file_input=
declare file_output=

source $file_data
eval "echo \"$(< $file_input)\"" > $file_output

./parser.sh data.sh template.txt parsed_file.txt

./parser.sh data.sh template.txt parsed_file.txt

parsed_file.txt

parsed_file.txt

Variable 1 value: value 1
Variable 2 value: value 2

回答by mklement0

Here's a robust Bash functionthat - despite using eval- should be safe to use.

这是一个强大的 Bash 函数,尽管使用了它,但使用起来eval应该是安全的。

All ${varName}variable references in the input text are expanded based on the calling shell's variables.

${varName}输入文本中的所有变量引用都基于调用 shell 的变量进行扩展。

Nothing elseis expanded: neither variable references whose names are notenclosed in {...}(such as $varName), nor command substitutions ($(...)and legacy syntax `...`), nor arithmetic substitutions ($((...))and legacy syntax $[...]).

没有其他扩展:名称包含在{...}(例如$varName)中的变量引用、命令替换($(...)和遗留语法`...`)、算术替换($((...))和遗留语法$[...])。

To treat a $as a literal, \-escape it; e.g.:\${HOME}

要将 a$视为文字,请\-escape 它;例如:\${HOME}

Note that input is only accepted via stdin.

请注意,输入只能通过stdin接受。

Example:

例子:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and $(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

Function source code:

函数源代码:

expandVarsStrict(){
  local line lineEscaped
  while IFS= read -r line || [[ -n $line ]]; do  # the `||` clause ensures that the last line is read even if it doesn't end with \n
    # Escape ALL chars. that could trigger an expansion..
    IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '')
    # ... then selectively reenable ${ references
    lineEscaped=${lineEscaped//$''{/${}
    # Finally, escape embedded double quotes to preserve them.
    lineEscaped=${lineEscaped//\"/\\"}
    eval "printf '%s\n' \"$lineEscaped\"" | tr '' '`([$'
  done
}

The function assumes that no 0x1, 0x2, 0x3, and 0x4control characters are present in the input, because those chars. are used internally - since the function processes text, that should be a safe assumption.

该函数假定输入中不存在0x10x20x30x4控制字符,因为这些字符。在内部使用 - 因为函数处理text,这应该是一个安全的假设。

回答by Thomas

here's my solution with perl based on former answer, replaces environment variables:

这是我根据以前的答案使用 perl 的解决方案,替换了环境变量:

perl -p -e 's/$\{(\w+)\}/(exists $ENV{}?$ENV{}:"missing variable ")/eg' < infile > outfile

回答by neu242

Create rendertemplate.sh:

创建rendertemplate.sh

#!/usr/bin/env bash

eval "echo \"$(cat )\""

And template.tmpl:

并且template.tmpl

Hello, ${WORLD}
Goodbye, ${CHEESE}

Render the template:

渲染模板:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar

回答by Beau Simensen

If you are open to using Perl, that would be my suggestion. Although there are probably some sedand/or AWKexperts that probably know how to do this much easier. If you have a more complex mapping with more than just dbName for your replacements you could extend this pretty easily, but you might just as well put it into a standard Perl script at that point.

如果您愿意使用Perl,那将是我的建议。尽管可能有一些sed和/或AWK专家可能知道如何更容易地做到这一点。如果您有一个更复杂的映射,而不仅仅是 dbName 用于替换,您可以很容易地扩展它,但此时您最好将其放入标准 Perl 脚本中。

perl -p -e 's/$\{dbName\}/testdb/s' yourfile | mysql

A short Perl script to do something slightly more complicated (handle multiple keys):

一个简短的 Perl 脚本来做一些稍微复杂的事情(处理多个键):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;

If you name the above script as replace-script, it could then be used as follows:

如果您将上述脚本命名为 replace-script,则可以按如下方式使用它:

replace-script < yourfile | mysql

回答by user976433

file.tpl:

文件.tpl:

The following bash function should only replace ${var1} syntax and ignore 
other shell special chars such as `backticks` or $var2 or "double quotes". 
If I have missed anything - let me know.

script.sh:

脚本.sh:

template(){
    # usage: template file.tpl
    while read -r line ; do
            line=${line//\"/\\"}
            line=${line//\`/\\`}
            line=${line//$/\$}
            line=${line//\${/${}
            eval "echo \"$line\""; 
    done < 
}

var1="*replaced*"
var2="*not replaced*"

template file.tpl > result.txt