繁体   English   中英

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

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

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

例如,以下工作完美

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的变量。

这里的问题是,当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消除重复,因为你只需要不同变量名列表。)

有了这个,您可以组装一个执行所有必要替换的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}来表示“将$varname的值作为变量名,并返回该变量的值。”这就是Bash参考的 §3.5.3“Shell参数扩展” 手动调用“间接扩展”。)

你可以将它包装在一个函数中:

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

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

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

用这个:

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

例如:

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

打印:

abcdef $variable blah

尝试使用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#也是标记,可以毫无问题地更改

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM