在模板文件中替换bash变量

4

我正在尝试使用Bash运行安装程序的某个形式。在此过程中,将复制配置文件并替换其中的某些值。下面是这样一个配置文件:

server {
    listen 80;
    root ${INSTALLPATH};
    server_name ${SITEURL};

    client_max_body_size 20m;
    client_body_timeout 120s;

    location / {
        try_files /public/router.php =404;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_pass ${PHPSERV};
        fastcgi_index router.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }

    location /assets {
        try_files /app/$uri =404;
    }

}

#Enables HTTPS access
#This requires you to install certificates and is not enabled by default
#If you wish to enable HTTPS, uncomment (remove the #s) from the below lines
#And change the ssl_certificate and ssl_certificate_key to point to the     correct
#certificates.

#server {
#    listen 443;
#    root ${INSTALLPATH};
#    server_name ${SITEURL};
#
#    ssl on;
#    ssl_certificate     /etc/nginx/ssl/site.crt;
#    ssl_certificate_key /etc/nginx/ssl/site.key;
#
#    location / {
#        try_files /public/router.php =404;
#        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
#        fastcgi_pass ${PHPSERV};
#        fastcgi_index router.php;
#        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#        include /etc/nginx/fastcgi_params;
#    }
#
#    location /assets {
#        try_files /app/$uri =404;
#    }
#
#}

我发现大多数示例都使用了 eval,我也尝试过使用它来进行替换,但是它似乎无法正常扩展此处的文件,并且 bash 尝试执行一些东西而失败了。

目前,我的代码如下:

INSTALLPATH="/var/www/html/mycustomsite/"
PHPSERV="127.0.0.1:9000"
SITEURL="example.com"

while read -r line; do
    eval echo -e "${line}"
done < template

然而,这并不能正确替换所声明的值,也不能正确生成文件。例如,它会丢失任何以 # 开头的行,并尝试执行其他行(同时丢失一些间距)。
使用仅 Bash 和大多数 Linux 系统上可用的命令,正确的方法是什么?

相关的编程内容:http://stackoverflow.com/questions/29487986/sed-find-replace-using-bash-arrays-fails/29488120#29488120 - Nikos M.
动态性是我部分考虑的因素,因为我希望在未来尽可能少地编辑替换代码,但这符合我所需的。 - Joshua Taylor
你可以轻松地调整模板标签格式,这是默认使用的格式,但只需更改一行即可。 - Nikos M.
1
相关主题:http://mywiki.wooledge.org/TemplateFiles - Charles Duffy
@CharlesDuffy 你是对的!我对我的第一个答案并不完全满意。我已经编辑了它们,增加了有关安全性和sed版本的说明! - F. Hauri - Give Up GitHub
显示剩余3条评论
1个回答

6

安全提示

这不解决安全问题!使用eval是有害的!

兼容性答案也不是更好的选择!

当然,您必须对模板的内容有信心!!

如果需要,尝试使用!(请参阅我的上一个回答)

仅限的快速方法:

下,您可以简单地执行以下操作:

eval "INSTALLPATH='/somepath/somewhere' SITEURL='example.com' PHPSERV='127.0.0.1:9000'; echo \"$(<template)\""

或者

eval "INSTALLPATH='/somepath/somewhere'
    SITEURL='example.com'
    PHPSERV='127.0.0.1:9000';
    echo \"$(<template)\""

由于您使用了 eval,因此可以将生成的配置文件存储到一个变量中:

eval "INSTALLPATH='/somepath/somewhere'
    SITEURL='example.com'
    PHPSERV='127.0.0.1:9000';
    cfgBody=\"$(<template)\""

那么

echo "$cfgBody"

和/或

echo "$cfgBody" >/cfgpath/cfgfile

将此放入循环中
tmplBody="$(<template)"
while read INSTALLPATH SITEURL PHPSERV CFGFILE;do
    [ "$CFGFILE" ] && eval "echo \"$tmplBody\"" >"$CFGFILE"
  done <<<"
    /somepath/somewhere            example.com  127.0.0.1:9000  /tmp/file1
    '/some\ other\ path/elsewhere' sample2.com  127.0.0.1:9001  /tmp/file2
"

注意: 在第二行中,有些空格是转义的(前面带有反斜杠\和引号')。反斜杠告诉read不要分割变量,而引号必须添加到生成的/tmp/file2中。

软方式(兼容答案)

下,您可以采取以下方式:

#!/bin/sh

(
    cat <<eohead
    #!/bin/sh
    INSTALLPATH='/somepath/somewhere'
    SITEURL='example.com'
    PHPSERV='127.0.0.1:9000';
    cat <<eof
eohead
    cat template
    echo eof
) | /bin/sh

这不需要bash,已在下测试过。

无需eval

sedcmd=''
for var in INSTALLPATH SITEURL PHPSERV;do
    printf -v sc 's/${%s}/%s/;' $var "${!var//\//\\/}"
    sedcmd+="$sc"
  done
sed -e "$sedcmd" <template

可以编写成循环:

while read INSTALLPATH SITEURL PHPSERV CFGFILE;do
    if  [ "$CFGFILE" ] ;then
        sedcmd=''
        for var in INSTALLPATH SITEURL PHPSERV;do
            printf -v sc 's/${%s}/%s/;' $var "${!var//\//\\/}"
            sedcmd+="$sc"
          done
        sed -e "$sedcmd" <template >"$CFGFILE"
      fi
  done <<<"
    /somepath/somewhere             example.com  127.0.0.1:9000  /tmp/file1
    '/some\ other\ path/elsewhere'  sample2.com  127.0.0.1:9001  /tmp/file2
"

兼容性答案,使用sed

这个方案可以在循环中运行,而不需要所谓的bashisms

#!/bin/sh

while read INSTALLPATH SITEURL PHPSERV CFGFILE;do

    sedcmd="s|\\\${INSTALLPATH}|${INSTALLPATH}|;"
    sedcmd="${sedcmd}s|\\\${SITEURL}|${SITEURL}|;"
    sedcmd="${sedcmd}s|\\\${PHPSERV}|${PHPSERV}|;"

    sed -e "$sedcmd" template >"$CFGFILE"

  done <<eof
    /somepath/somewhere             example.com  127.0.0.1:9000  /tmp/file1
    '/some\ other\ path/elsewhere'  sample2.com  127.0.0.1:9001  /tmp/file2
eof

比较输出:

diff -u99 /tmp/file{1,2}
--- /tmp/file1        2015-05-31 11:02:03.407463963 +0200
+++ /tmp/file2        2015-05-31 11:02:03.407463963 +0200
@@ -1,22 +1,22 @@
 server {
     listen 80;
-    root /somepath/somewhere;
-    server_name example.com;
+    root '/some other path/elsewhere';
+    server_name sample2.com;

     client_max_body_size 20m;
     client_body_timeout 120s;

     location / {
         try_files /public/router.php =404;
         fastcgi_split_path_info ^(.+?\.php)(/.*)$;
-        fastcgi_pass 127.0.0.1:9000;
+        fastcgi_pass 127.0.0.1:9001;
         fastcgi_index router.php;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
         include /etc/nginx/fastcgi_params;
     }

     location /assets {
         try_files /app/$uri =404;
     }

 }

由于我在其他地方使用这些变量,我已将其更改为eval echo "\"$(<template)\"",似乎也可以正确处理它。但是,由于我在其他部分使用bash,如果bash更干净,则它运行得更好。 - Joshua Taylor
注意:cat <<eohead 后面的 shebang /bin/sh 是无用的,但我觉得这样更清晰。 - F. Hauri - Give Up GitHub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接