rnp
rnp

Reputation: 65

Replacing a string with the value of a variable using shell script

I want to replace a string in a file with the value of a variable. I a string lvl in the template file prm.prm which needs to be replaced by the value of SLURM_ARRAY.

I tried using

sed -i 's/lvl/${SLURM_ARRAY}/' prm.prm

This replaces the string lvl with ${SLURM_ARRAY} and not its value. How can I rectify this?

Upvotes: 0

Views: 1232

Answers (2)

ikegami
ikegami

Reputation: 386696

Every character between single quotes is used literally.

You could use double quotes instead as follows:

sed -i "s/lvl/${SLURM_ARRAY}/" prm.prm

However, your code now suffers from a code injection bug. There are characters (e.g. /) that will cause problems if found in the value of SLURM_ARRAY. To avoid this, these characters needs to be escaped.

quotemeta() { printf %s "$1" | sed 's/\([^a-zA-Z0-9]\)/\\\1/g'; }

sed -i "s/lvl/$( quotemeta "$SLURM_ARRAY" )/" prm.prm

However, it would be best to avoid generating programs from the shell. But that would require avoiding sed since it doesn't provide the necessary tools. For example, a couple of Perl solutions:

perl -i -spe's/lvl/$s/' -- -s="$SLURM_ARRAY" prm.prm
S="$SLURM_ARRAY" perl -i -pe's/lvl/$ENV{S}/' prm.prm

Upvotes: 1

dan
dan

Reputation: 5251

Replace pattern with the value of an environment variable, with no extra interpolation of the contents:

Using perl:

export SLURM_ARRAY
perl -pe's/lvl/$ENV{SLURM_ARRAY}/g' prm.prm

Using awk:

export SLURM_ARRAY

awk '
{
    if (match($0, /lvl/)) {
        printf "%s", substr($0, 1, RSTART - 1)
        printf "%s", ENVIRON["SLURM_ARRAY"]
        print substr($0, RSTART + RLENGTH)
    }
    else {
        print
    }
}
' prm.prm

There's also SLURM_ARRAY=$SLURM_ARRAY perl ...etc or similar, to set the environment of a single process.

It can also be done with the variable as an argument. With both perl and awk you can access and modify the ARGV array. For awk you have to reset it so it's not processed as a file. The perl version looks like perl -e 'my $r = $ARGV[0]; while (<STDIN>) {s/lvl/$r/g; print}' "$SLURM_ARRAY" < prm.prm. It looks even better as perl -spe's/lvl/$r/g' -- -r="$SLURM_ARRAY". Thanks to ikegami.

For awk, I should say that the reason for not using awk -v r=foo is the expansion of C escapes. You could also read the value from a file (or bash process substitution).

Upvotes: 0

Related Questions