替换模板文件中的bash变量

时间:2022-09-24 22:12:18

I am attempting to use Bash in order to run a form of an install process. During this process, a configuration file is copied and certain values are replaced inside of it. Such a config can be found below:

我试图使用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;
#    }
#
#}

I have been finding that most examples stem off the use of eval, and I have tried using that to do this replacement, however it appears that the file here does not properly expand, and bash tries to execute some stuff and can't.

我发现大多数例子都是使用eval,我尝试使用它来替换它,但是看起来这里的文件没有正确扩展,而bash试图执行一些东西而不能。

Currently, I have this

目前,我有这个

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

However, this does not properly replace the values that are declared, nor does it generate the file correctly. It loses for example any line that starts with # and tries to execute others (along with losing some spacing).

但是,这不能正确替换声明的值,也不能正确生成文件。它失去了例如以#开头并试图执行其他行的任何行(以及丢失一些间距)。

What would be the correct way to do this, using just Bash and commands available on most Linux systems?

使用Bash和大多数Linux系统上可用的命令,这样做的正确方法是什么?

1 个解决方案

#1


4  

Security tips

This don't take care of security issues! Using eval is evil!

这不关心安全问题!使用eval是邪恶的!

The compatible answer is not better!

兼容的答案并不是更好!

Of course, you have to be confident about the content of your template!!

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

If else, try using ! (see my last answer)

如果不是,请尝试使用sed! (见我的上一个答案)

Quick way only!:

Under you can simply:

在bash下你可以简单地:

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

or

要么

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

As you're using eval, you could store your resulting config file into one variable:

当您使用eval时,您可以将生成的配置文件存储到一个变量中:

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

Then

然后

echo "$cfgBody"

and/or

和/或

echo "$cfgBody" >/cfgpath/cfgfile

Doing this into a loop

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
"

Note: On second line, there are escaped spaces (prepanded with a backshash \ and quotes '. The backslash tell read to not split the variable, and the quotes have to be added into the resulting /tmp/file2.

注意:在第二行,有转义空格(预装了后缀\和引号'。反斜杠告诉读取不拆分变量,并且引号必须添加到生成的/ tmp / file2中。

Soft way (compatible answer)

Under , you may do this way:

在posix shell下,你可以这样做:

#!/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

This don't require bash, was tested under and .

这不需要bash,在dash和busybox下测试。

Without eval !

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

Could work into a loop:

可以进入一个循环:

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
"

Compatible answer, using sed

This could work without so called bashisms, into a loop:

这可以在没有所谓的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

Comparing outputs:

比较输出:

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;
     }

 }

#1


4  

Security tips

This don't take care of security issues! Using eval is evil!

这不关心安全问题!使用eval是邪恶的!

The compatible answer is not better!

兼容的答案并不是更好!

Of course, you have to be confident about the content of your template!!

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

If else, try using ! (see my last answer)

如果不是,请尝试使用sed! (见我的上一个答案)

Quick way only!:

Under you can simply:

在bash下你可以简单地:

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

or

要么

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

As you're using eval, you could store your resulting config file into one variable:

当您使用eval时,您可以将生成的配置文件存储到一个变量中:

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

Then

然后

echo "$cfgBody"

and/or

和/或

echo "$cfgBody" >/cfgpath/cfgfile

Doing this into a loop

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
"

Note: On second line, there are escaped spaces (prepanded with a backshash \ and quotes '. The backslash tell read to not split the variable, and the quotes have to be added into the resulting /tmp/file2.

注意:在第二行,有转义空格(预装了后缀\和引号'。反斜杠告诉读取不拆分变量,并且引号必须添加到生成的/ tmp / file2中。

Soft way (compatible answer)

Under , you may do this way:

在posix shell下,你可以这样做:

#!/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

This don't require bash, was tested under and .

这不需要bash,在dash和busybox下测试。

Without eval !

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

Could work into a loop:

可以进入一个循环:

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
"

Compatible answer, using sed

This could work without so called bashisms, into a loop:

这可以在没有所谓的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

Comparing outputs:

比较输出:

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;
     }

 }