Reputation: 185
I have a file that holds token variables. When we switch environments we want to replace those tokens correctly for the environment we're in.
How can I do this in a Linux shell? I'm thinking awk or sed.
Example:
File has this data:
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__
I want to read this file, recognize the __HOST__
and replace it with the environment variable $(HOST)
. We would get the environment variable name from the matched string between the two underscores.
Does anyone have a good idea how we could do this?
Upvotes: 7
Views: 9966
Reputation: 6615
Here's what you want:
while IFS= read -r line || [[ -n "$line" ]]; do
from=$(echo $line | cut -d '=' -f 1 )
to=$(echo $line | cut -d '=' -f 2 )
sedString=${sedString}" s/__${from}__/${to}/g;"
done < tokenFile.txt
sed "${sedString}" old.txt >> new.txt
It builds a string with all the replacements you want from tokenFile.txt, then replaces them all at once with one sed command, in whatever file you specify. tokenFile.txt has your 'old=new' pairs. old.txt is the files with tokens. new.txt is where you output.
So for example, after running the script you get:
$ cat tokenFile.txt
DB_HOST=127.0.0.1
$ cat old.txt
dbconnect __DB_HOST__
$ cat new.txt
dbconnect 127.0.0.1
Upvotes: 2
Reputation: 11
I wrote a bash script cause I did not have envsubst
available. I used grep
to find my token pattern then sed
to make the replacements. Note the use of bash indirect expansion
.
Given file replace-vars.txt
:
Craig likes ${ENV_1}
Anuj likes ${ENV_2}
Dave likes ${ENV_3}
Run the following script:
#!/usr/bin/env bash
# env variables to replace
export ENV_1=milk
export ENV_2=cheese
export ENV_3=candy
# File to modify
declare FILE_TO_REPLACE=replace-vars.txt
# Pattern for your tokens -- e.g. ${token}
declare TOKEN_PATTERN='(?<=\$\{)\w+(?=\})'
# Find all tokens to replace
declare TOKENS=$(grep -oP ${TOKEN_PATTERN} ${FILE_TO_REPLACE} | sort -u)
# Loop over tokens and use sed to replace
for token in $TOKENS
do
echo "Replacing \${${token}} with ${!token}"
sed -i "s/\${${token}}/${!token}/" ${FILE_TO_REPLACE}
done
Output:
Replacing ${ENV_1} with milk
Replacing ${ENV_2} with cheese
Replacing ${ENV_3} with candy
Modified replace-vars.txt
:
Craig likes milk
Anuj likes cheese
Dave likes candy
Upvotes: 1
Reputation: 241861
The program envsubst
was designed for roughly this purpose, but it wants to to use standard shell format for the strings to be substituted from the environment:
# Here-doc delimiter quoted to avoid having the shell do the substitutions
$ HOST=myhost PASSWORD=secret envsubst <<"EOF"
DB_HOST=$HOST
DB_PASSWORD=${PASSWORD}
EOF
DB_HOST=myhost
DB_PASSWORD=secret
envsubst
is part of Gnu gettext, and is widely available. If you really need to use the __...__
syntax, it would be easy to change it to envsubst
syntax by piping through sed:
$ cat myfile.txt
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__
$ sed -E 's/__(([^_]|_[^_])*)__/${\1}/g' myfile.txt |envsubst
Alternatively, if you have Python, there is a very simple solution:
from sys import stdin
from os import getenv
import re
pattern = re.compile(r"__(\w*)__")
def replacer(m):
val = getenv(m[1])
if val is None:
# No such environment
# Perhaps produce a warning, or an error
# Here we just pass through the string unaltered
return m[0]
return val
print(pattern.sub(replacer, stdin.read()))
Example:
$ export HOST=myhost PASSWORD=secret
$ python3 subst.py < myfile.txt
DB_HOST=myhost
DB_PASSWORD=secret
The key to this solution is that Python (and only a few other languages, unfortunately) allows the replacement argument of pattern substitution to be a function, which is then called on each successive match to produce the replacement text. That makes writing functions like this easier in Python than in awk, for example, where the replacement text is fixed.
Upvotes: 12
Reputation: 123550
This is what m4
is meant for:
$ export HOST=myhost PASSWORD=mypassword
$ cat myfile
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__
$ m4 -D __HOST__="$HOST" -D __PASSWORD__="$PASSWORD" myfile
DB_HOST=myhost
DB_PASSWORD=mypassword
If you want to autodefine macros for all environment variables, that's of course possible if slightly iffy:
$ cat mymacros.m4
esyscmd(`env | sed -ne "s/^\([A-Z_]*\)=\(.*\)/define(__\1__, \`\2')/p" | tr -d "\n"')
$ cat myfile2
include(`mymacros.m4')
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__
And other arbitrary text containing things like "My editor is __EDITOR__"
$ m4 myfile2
DB_HOST=myhost
DB_PASSWORD=mypassword
And other arbitrary text containing things like "My editor is vim"
It's not necessarily the simplest way of doing it, but it's highly flexible and extensible, with support for conditional statements, macro functions and more.
Upvotes: 5