Marco
Marco

Reputation: 622

Using sed after cat << 'EOT' to substitute just one variable inside the generated script

I'm building a script for php-fpm compilation, installation and deployment in ubuntu 14. At one point, I have got to generate another file using this main script. The resulting file is a script and should have all variables BUT one NOT expanded.

So I started with cat << 'EOT' in will of resolving the thing after the file generation with sed. But I find myself in a "logic" blackhole.

As for the EOT quoting beeing an issue for expanding just one variable, the same is for the sed line. I went straight writing the following, then laught at it without even executing it, of course.

sed -i 's/\$PhpBuildVer\/$PhpBuildVer' /etc/init.d/php-$PhpBuildVer-fpm

OR

sed -i "s/\$PhpBuildVer\/$PhpBuildVer" /etc/init.d/php-$PhpBuildVer-fpm

both would fail, while I need the first pattern to be the "$PhpBuildVer" itself and the other one beeing the expanded variable, for instance, 7.1.10.

How would I perform this substituion with either sed or another GNU Linux command?

This is my script, most of the parts have been cut-off as non question related.

#!/bin/bash
PhpBuildVer="7.1.10"

... #removed non relevant parts of the script 
cat << 'EOT' >> /etc/init.d/php-$PhpBuildVer-fpm
#! /bin/sh
### BEGIN INIT INFO
# Provides:          php-$PhpBuildVer-fpm
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts php-$PhpBuildVer-fpm
# Description:       starts the PHP FastCGI Process Manager daemon
### END INIT INFO
php_fpm_BIN=/opt/php-$PhpBuildVer/sbin/php-fpm
php_fpm_CONF=/opt/php-$PhpBuildVer/etc/php-fpm.conf
php_fpm_PID=/opt/php-$PhpBuildVer/var/run/php-fpm.pid
php_opts="--fpm-config $php_fpm_CONF"

wait_for_pid () {
        try=0
        while test $try -lt 35 ; do
                case "$1" in
                        'created')
                        if [ -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                        'removed')
                        if [ ! -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                esac
                echo -n .
                try=`expr $try + 1`
                sleep 1
        done
}
case "$1" in
        start)
                echo -n "Starting php-fpm "
                $php_fpm_BIN $php_opts
                if [ "$?" != 0 ] ; then
                        echo " failed"
                        exit 1
                fi
                wait_for_pid created $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        stop)
                echo -n "Gracefully shutting down php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -QUIT `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed. Use force-exit"
                        exit 1
                else
                        echo " done"
                       echo " done"
                fi
        ;;
        force-quit)
                echo -n "Terminating php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -TERM `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        restart)
                $0 stop
                $0 start
        ;;
        reload)
                echo -n "Reload service php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -USR2 `cat $php_fpm_PID`
                echo " done"
        ;;
        *)
                echo "Usage: $0 {start|stop|force-quit|restart|reload}"
                exit 1
        ;;
esac
EOF

#Here the variable should be substituted.
chmod 755 /etc/init.d/php-$PhpBuildVer-fpm
... #removed non relevant parts of the script 

Upvotes: 1

Views: 4294

Answers (2)

William Pursell
William Pursell

Reputation: 212218

Just break up the heredoc. eg

cat > file << 'EOF'
This line will not be interpolated: $FOO
EOF
cat >> file << EOF
and this line will: $FOO
EOF

If for some reason you do want to used sed as well, don't do it after, just use it instead of cat:

sed 's@foo@bar@g' >> file << EOF
this line's foo is changed by sed, with interpolated $variables
EOF

Upvotes: 2

cxw
cxw

Reputation: 17041

I am not 100% sure, but I think what you are looking for is:

sed -i 's/\$PhpBuildVer/'"$PhpBuildVer"'/' /etc/init.d/php-$PhpBuildVer-fpm

You can actually put two quoted expressions right next to each other in bash. E.g., echo '12'"34"'56' will output 123456. In this case, the first \$PhpBuildVer is in '' so it can match literally, and the second is in "" so that it will be expanded.

(But maybe you should consider using a template file and php, or (blatant plug) perlpp* to build the script, rather than inlining all the text into your main script. ;) )

Edit by the way, using cat ... >> rather than cat ... > means you will be appending to the script unless you have rmed it somewhere in the code you didn't show.

Edit 2 If $PhpBuildVer has any characters in it that sed interprets in the replacement text, you might need to escape it:

repl_text="$(sed -e 's/[\/&]/\\&/g' <<<"$PhpBuildVer")"
sed -i 's/\$PhpBuildVer/'"$repl_text"'/' /etc/init.d/php-$PhpBuildVer-fpm

Thanks to this answer by Pianosaurus.

Tested example

I put this in make.sh:

#!/bin/bash

f=42     # The variable we are going to substitute
cat <<'EOT' >"test-$f.sh"    # The script we are generating
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
EOT

echo "test-$f.sh before substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"

sed -i 's/\$f/'"$f"'/' "test-$f.sh"    # The substitution, from above

echo "test-$f.sh after substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"

The output I get is:

test-42.sh before substitution is:
---------
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
---------

(note the literal $f)

test-42.sh after substitution is:
---------
#!/bin/sh
# Provides: test-42.sh
echo 'Hello, world!'
---------

(now the $f is gone, and has been replaced with its value, 42)

perlpp example

Since *I am presently the maintainer of perlpp, I'll give you that example, too :) . In a template file that I called test.template, I put:

#!/bin/sh
# Provides: test-<?= $S{ver} ?>.sh
echo 'Hello, world!'

That was exactly the content of the script I wanted, but with <?= $S{ver} ?> where I wanted to do the substitution. I then ran

perlpp -s ver=\'7.1.10\' test.template

(with escaped quotes to pass them to perl) and got the output:

#!/bin/sh
# Provides: test-7.1.10.sh
echo 'Hello, world!'

:)

  • Any -s name=\'value\' command-line argument to perlpp creates $S{name}, which you can refer to in the template.
  • <?= expr ?> prints the value of expression expr
  • Therefore, <?= $S{name} ?> outputs the value given on the command line for name.

Upvotes: 3

Related Questions