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

时间:2013-10-25 05:05:18

标签: regex bash sed

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

e.g。以下工作完美

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

所以,如果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>

变,

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>

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

我知道它与反向引用有关但我无法弄清楚如何使用反向引用的内容作为变量名。

我试过了,

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

但这无效,因为它似乎是在寻找一个名为$ \ 1的变量。

4 个答案:

答案 0 :(得分:3)

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

或者,从更高级别的观点来看,问题是sed不知道Bash变量,所以你需要Bash来提供变量的细节,但是Bash没有了解sed替换,因此它无法知道您需要哪些变量的详细信息。

只要你想使用Bash变量,修复就是使用更多Bash:你需要在第一次调用sed之前识别相关的变量名。以下显示了如何做到这一点。


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

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

(第一个grep获取表单%...%的所有表达式。第二个grep过滤掉百分号;或者您可以使用sed来表示您更喜欢。sort -u消除了重复项,因为您只需要 distinct 变量名列表。)

有了这个,您可以组装一个执行所有必要替换的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

(注意使用${!varname}表示&#34;将$varname的值作为变量名,并返回该变量的值。&#34;这就是{ {3}}调用&#34;间接扩展&#34;。)

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

function replace_bash_variables () {
    local file="$1"
    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

您还可以调整上述内容以进行逐行处理,这样就不需要两次读取文件。 (这为您提供了更大的灵活性,因为读取文件两次意味着您必须传入实际文件,并且不能(比方说)将此应用于管道的输出。)

答案 1 :(得分:0)

使用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>

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

答案 2 :(得分:0)

使用此:

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

例如:

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

打印:

abcdef $variable blah

答案 3 :(得分:0)

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

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

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

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

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

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

# and next
   t varxs


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

# display the result
   p
   }
"
  • 限制在这里由于使用了char“²”没有被转义所以如果²出现在文件中,可能会很烦人(所以将这个char更改为标记或将其翻译成文件)
  • #V0r:和:r0V#也是标记,可以毫无问题地进行更改