bash 使用 sed 将文件中的字符串替换为该名称变量的内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19581495/
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-18 08:24:26  来源:igfitidea点击:

Using sed to replace string in file with contents of variable of that name

regexbashsed

提问by richard

I'm trying to use sed to replace template strings in files of the form %XXX% with the value of a variable called XXX in my shell script.

我正在尝试使用 sed 将 %XXX% 形式的文件中的模板字符串替换为我的 shell 脚本中名为 XXX 的变量的值。

e.g. The following works perfectly

例如以下工作完美

sed "s/%user_home%/$user_home/gi"

So if user_home=fredthe following,

所以如果user_home=fred以下,

NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/%user_home%_webapp_error.log
  CustomLog /var/log/apache2/%user_home%_webapp.log common

  DocumentRoot /home/%user_home%/web_app/public
</VirtualHost>

becomes,

变成,

NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/fred_webapp_error.log
  CustomLog /var/log/apache2/fred_webapp.log common

  DocumentRoot /home/fred/web_app/public
</VirtualHost>

The problem is that I want to run the sed command without explicitly knowing the template strings and their variables up front. That is, it looks for %XXX% and then replaces that with the contents of $XXX without caring what the actual name of the variable is.

问题是我想在不明确知道模板字符串及其变量的情况下运行 sed 命令。也就是说,它会查找 %XXX%,然后将其替换为 $XXX 的内容,而无需关心变量的实际名称是什么。

I know its got something to do with back-references but I can't figure out how to use the content of a back-reference as the variable name.

我知道它与反向引用有关,但我不知道如何使用反向引用的内容作为变量名。

I tried,

我试过,

sed "s/%\([a-z_]\)%/$()/gi"

but this failed to work because it seems to be a looking for a variable called $\1.

但这未能奏效,因为它似乎是在寻找名为 $\1 的变量。

回答by ruakh

The problem here is that by the time the sedcommand is actually run (and therefore by the time it retrieves the variable-name), the sedcommand must have been fully assembled (including substituting the Bash variable's value into the replacement string); so everything happens in the wrong order.

这里的问题是,当sed命令实际运行时(因此当它检索变量名时),sed命令必须已经完全组装(包括将 Bash 变量的值替换为替换字符串);所以一切都以错误的顺序发生。

Or, taking a higher-level view, the problem is that seddoesn't know about Bash variables, so you need Bash to provide the details of the variables, but Bash doesn't know about sedreplacements, so it doesn't have any way of knowing what variables you need the details of.

或者,从更高层次来看,问题是sed不知道 Bash 变量,所以需要 Bash 提供变量的详细信息,但是 Bash 不知道sed替换,所以它没有任何办法了解您需要哪些变量的详细信息。

The fix, as long as you want to use Bash variables, is to use more Bash: you need to identify the relevant variable-names before you first call sed. The below shows how you can do that.

只要您想使用 Bash 变量,修复方法就是使用更多的 Bash:您需要在第一次调用sed. 下面显示了如何做到这一点。



To get the list of all variable-names in your file, you can write something like this:

要获取文件中所有变量名的列表,您可以编写如下内容:

grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u

(The first grepgets all expressions of the form %...%. The second grepfilters out the percent-signs; or you can use sedfor that, if you prefer. The sort -ueliminates the duplicates, since you only need the list of distinctvariable-names.)

(第一个grep获取形式的所有表达式%...%。第二个grep过滤掉百分号;或者您可以使用sed它,如果您愿意。sort -u消除重复项,因为您只需要不同变量名的列表。)

Armed with that, you can assemble a sedcommand that performs all the necessary replacements:

有了它,您就可以组装一个sed执行所有必要替换的命令:

sed_args=()
while read varname ; do
    sed_args+=(-e "s/%$varname%/${!varname}/g")
done < <(grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u)
sed "${sed_args[@]}" FILE

(Note the use of ${!varname}to mean "take the value of $varnameas a variable-name, and return the value of that variable." This is what §3.5.3 "Shell Parameter Expansion" of the Bash Reference Manualcalls "indirect expansion".)

(注意使用${!varname}来表示“将 的值$varname作为变量名,并返回该变量的值。”这就是Bash 参考手册的第 3.5.3 节“Shell 参数扩展”所称的“间接扩展”。 )

You can wrap this in a function:

您可以将其包装在一个函数中:

function replace_bash_variables () {
    local file=""
    local sed_args=()
    local varname
    while read varname ; do
        sed_args+=(-e "s/%$varname%/${!varname}/g")
    done < <(grep -o '%[a-z_][a-z_]*%' "$file" | grep -o '[a-z_][a-z_]*' | sort -u)
    if [[ "${#sed_args[@]}" = 0 ]] ; then
        # if no variables to replace, just cat the file:
        cat -- "$file"
    else
        sed "${sed_args[@]}" -- "$file"
    fi
}

replace_bash_variables OLD_FILE > NEW_FILE

You can also adjust the above to do line-by-line processing, so that it doesn't need to read the file twice. (That gives you more flexibility, since reading the file twice means you have to pass in the actual file, and can't (say) apply this to the output of a pipeline.)

也可以调整上面的做逐行处理,这样就不需要读取文件两次了。(这为您提供了更大的灵活性,因为读取文件两次意味着您必须传入实际文件,并且不能(例如)将其应用于管道的输出。)

回答by mvp

Use this:

用这个:

sed -E "s/%(\w+)%/$/g"

For example this:

例如这个:

echo "abcdef %variable% blah" | sed -E "s/%(\w+)%/$/g"

prints:

印刷:

abcdef $variable blah

回答by NeronLeVelu

try with 1 sed but still need previously to catch the "set" content to know variables name and value

尝试使用 1 sed 但仍需要先前捕获“设置”内容以了解变量名称和值

#!/bin/ksh
# YourFilename contain the file name of your file to treat (here passed as 1st parameter to a script)
YourFileName=

(set | sed 's/.*/#V0r:&:r0V#/'; cat ${YourFileName}) | sed -n "
s/$/2/
H

$  {
   x
   s/^\(\n *\)*//
# also reset t flag
   t varxs

:varxs
   s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^2]*\):r0V#2\(\n.*\)%%/#V0r:=:r0V#2/
   t varxs
: tmpb

# clean the line when no more occurance in text
#   s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^2]*\):r0V#2\n//
   s/^[^2]*:r0V#2\n//

# and next
   t varxs


# clean the  marker
   s/2\(\n\)//g
   s/2$//

# display the result
   p
   }
"
  • limitation here due to the use of char "2" not escaped so if 2 appear in the file, could be annoying (so change this char as marker or translate it in the file)
  • #V0r: and :r0V# are marker also and could be changed without problem
  • 由于使用了字符“2”而没有转义,所以如果 2 出现在文件中,可能会很烦人(因此将此字符更改为标记或在文件中翻译它)
  • #V0r: 和 :r0V# 也是标记,可以毫无问题地更改

回答by Jotne

Using awkyou can do this

使用awk你可以做到这一点

awk '{gsub(/%user_home%/,"${user_home}")}1' file
NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/${user_home}_webapp_error.log
  CustomLog /var/log/apache2/${user_home}_webapp.log common

  DocumentRoot /home/${user_home}/web_app/public
</VirtualHost>

This replace the %user_home%to the variable ${user_home}

这将替换%user_home%为变量${user_home}