Environment variable substitution in sed
Your two examples look identical, which makes problems hard to diagnose. Potential problems:
You may need double quotes, as in
sed 's/xxx/'"$PWD"'/'
$PWD
may contain a slash, in which case you need to find a character not contained in$PWD
to use as a delimiter.
To nail both issues at once, perhaps
sed 's@xxx@'"$PWD"'@'
sed | replace value with environment variable containing double quotes
Using envsubst
:
Assume you have a string STRING
which you want to replace with the variable VARIABLE_STRING
. Then I would suggest the following approach:
- substitute all variables of interest with a proper shell variable name (include the
$
) export
all variables of interest- Use
envsubst
to perform the substitution
Here is an example:
$ export VARIABLE_STRING='foo "bar" qux'
$ echo "This is the string 'STRING'" | sed 's/\bSTRING\b/$VARIABLE_STRING/g' | envsubst '$VARIABLE_STRING'
This is the string 'foo "bar" qux'
You can adapt this to your liking. Please read [U&L] Replacing only specific variables with envsubst for further information.
Using GNU awk:
If you cannot make use of envsubst
, you can write something in awk. GNU awk can access the environment using the ENVIRON
array which is a map from variable name to variable value. Example:
awk 'BEGIN{print ENVIRON["HOME"]}'
This will print the content of $HOME
So now you could something like this:
- Create a map from string to replace into environment
- Loop over the names:
This would look something like this:
awk 'BEGIN{ map["NAME1"]="ENVIRONMENT_NAME1"
map["NAME2"]="ENVIRONMENT_NAME2"
map["NAME3"]="ENVIRONMENT_NAME3" }
{ for(i in map) gsub("/<" i "/>",ENVIRON[map[i]]) }
{ print }' file
Similar to the sed
in the first solution, I've added a way to ensure that the string NAME1FOO
is not being replaced by ENVIRONMENT_NAME1FOO
. This by adding word_boundaries /<
and />
in the regex.
**Note: this method might create loops. Imagine you have an environment variable:
ENVIRONMENT_NAME1="NAME2"
Then the string NAME1
might be replaced with the content of ENVIRONMENT_NAME2
. I say might because everything depends on the order of transversal in the for loop which is awk dependent.
Sed string replace using environment variable
This steals from the answer to How can I interpret variables on the fly in the shell script? -- this question isn't quite a duplicate of that one.
Provided that the variables are part of the environment:
export FRUIT="APPLE"
export TIMES="DAILY"
You can use the envsubst
GNU tool to perform the variable replacement:
echo 'The $$FRUIT$$ is good for health. Everyone should eat $$FRUIT$$ $$TIMES$$.' | sed -E 's/\$\$([A-Z]+)\$\$/${\1}/g' | envsubst
The APPLE is good for health. Everyone should eat APPLE DAILY.
Note that you need to put a backslash before the $
character since this character has a meaning for sed.
sed substitution with Bash variables
Variables inside '
don't get substituted in Bash. To get string substitution (or interpolation, if you're familiar with Perl) you would need to change it to use double quotes "
instead of the single quotes:
# Enclose the entire expression in double quotes
$ sed "s/draw($prev_number;n_)/draw($number;n_)/g" file.txt > tmp
# Or, concatenate strings with only variables inside double quotes
# This would restrict expansion to the relevant portion
# and prevent accidental expansion for !, backticks, etc.
$ sed 's/draw('"$prev_number"';n_)/draw('"$number"';n_)/g' file.txt > tmp
# A variable cannot contain arbitrary characters
# See link in the further reading section for details
$ a='foo
bar'
$ echo 'baz' | sed 's/baz/'"$a"'/g'
sed: -e expression #1, char 9: unterminated `s' command
Further Reading:
- Difference between single and double quotes in Bash
- Is it possible to escape regex metacharacters reliably with sed
- Using different delimiters for sed substitute command
- Unless you need it in a different file you can use the -i flag to change the file in place
Replacing text in a file using sed with an environment variable
You may try this:
find . -type f -name "*.js" -exec sed -i "s~http://localhost:3010~$SERVER~g" {} +
So using double quotes so that shell can expand $SERVER
and also used alternate delimiter ~
because /
is part of your variable.
sed variable substitution using string
You have two choices here. You can either evaluate $subdir
to build up a fixed sed
command that you then set as your filter; or you can evaluate a variable in the shell fragment that git filter-branch
will invoke.
To understand the latter, realize that your --index-filter
string becomes an ordinary shell variable:
--index-filter)
filter_index="$OPTARG"
;;
which is then passed to eval
:
eval "$filter_index" < /dev/null ||
die "index filter failed: $filter_index"
The eval
means that the expression in $filter_index
, set from your --index-filter
argument, has access to all the shell variables and functions in the filter-branch script. Unfortunately, none of its private variables holds the expression you'd like—but you can access its environment variables, which means you can put the value into an environment variable. In other words, you could supply subdir=<whatever>
as an environment to your original expression.
In any case, as bk2204 answered, you need to remove the -i
option. Besides that, some versions of sed
don't accept \t
as a tab character (presumably yours does, just be aware of this).
To expand the variable earlier, just do that. For instance:
... --index-filter \
'git ls-files -s | sed "s-\t\"*-&'$folder'/-" | ...
(I've removed the -i
here myself). Note how this exits single quotes, expands $folder
, then re-enters single quotes. If $folder
might contain whitespace, be sure to use double quotes while expanding it here:
... --index-filter \
'git ls-files -s | sed "s-\t\"*-&'"$folder"'/-" | ...
The nesting of quotes here is pretty tricky: the stuff inside the single quotes is all one big string, provided as the argument that sets the variable $filter_index
inside the filter-branch
script. The eval
runs it through a second pass of evaluation, breaking up into the pipeline (git ls-files
, piped to sed
, piped to git update-index
) and running the various multiple commands, all of which have their stdin redirected to /dev/null
.
Sed replace value in index.html by environment variable with slashes keeping enclosing text intact
You can use
sed -i 's/\(window\.API_URL =\)[^<]*/\1 "'"${MY_API_URL//\//\\\/}"'";/' index.html
See the online demo.
Details:
\(window\.API_URL =\)[^<]*
- a POSIX BRE pattern that matches and captures into Group 1window.API_URL =
text and then matches any zero or more chars other than<
\1 "'"${MY_API_URL//\//\\\/}"'";
- a replacement that puts back the value captured into Group 1 (\1
) and then adds a space,"
, theMY_API_URL
value and then";
string.
The ${MY_API_URL//\//\\\/}
is an example of variable expansion where a /
is replaced with \/
(thanks to //
after the variable name, all occurrences are replaced).
Using Sed to expand environment variables inside files
Consider your trial version:
cat test | sed -e "s/\(\${[A-Z]*}\)/`eval "echo '\1'"`/" > outputfile
The reason this doesn't work is because it requires prescience on the part of the shell. The sed script is generated before any pattern is matched by sed, so the shell cannot do that job for you.
I've done this a couple of ways in the past. Normally, I've had a list of known variables and their values, and I've done the substitution from that list:
for var in PATH VARIABLE USERNAME
do
echo 's%${'"$var"'}%'$(eval echo "\$$var")'%g'
done > sed.script
cat test | sed -f sed.script > outputfile
If you want to map variables arbitrarily, then you either need to deal with the whole environment (instead of the fixed list of variable names, use the output from env
, appropriately edited), or use Perl or Python instead.
Note that if the value of an environment variable contains a slash in your version, you'd run into problems using the slash as the field separator in the s/// notation. I used the '%' since relatively few environment variables use that - but there are some found on some machines that do contain '%' characters and so a complete solution is trickier. You also need to worry about backslashes in the value. You probably have to use something like '$(eval echo "\$$var" | sed 's/[\%]/\\&/g')
' to escape the backslashes and percent symbols in the value of the environment variable. Final wrinkle: some versions of sed
have (or had) a limited capacity for the script size - older versions of HP-UX had a limit of about 100. I'm not sure whether that is still an issue, but it was as recently as 5 years ago.
The simple-minded adaptation of the original script reads:
env |
sed 's/=.*//' |
while read var
do
echo 's%${'"$var"'}%'$(eval echo "\$$var" | sed 's/[\%]/\\&/g')'%g'
done > sed.script
cat test | sed -f sed.script > outputfile
However, a better solution uses the fact that you already have the values in the output from env
, so we can write:
env |
sed 's/[\%]/\\&/g;s/\([^=]*\)=\(.*\)/s%${\1}%\2%/' > sed.script
cat test | sed -f sed.script > outputfile
This is altogether safer because the shell never evaluates anything that should not be evaluated - you have to be so careful with shell metacharacters in variable values. This version can only possibly run into any trouble if some output from env
is malformed, I think.
Beware - writing sed
scripts with sed
is an esoteric occupation, but one that illustrates the power of good tools.
All these examples are remiss in not cleaning up the temporary file(s).
Related Topics
How to Use Sed to Extract Substring
When Should I Wrap Quotes Around a Shell Variable
Glibc Scanf Segmentation Faults When Called from a Function That Doesn't Align Rsp
What's the Magic of "-" (A Dash) in Command-Line Parameters
Retaining File Permissions With Git
How to Use Local Docker Images With Minikube
The Difference Between Fork(), Vfork(), Exec() and Clone()
Why Is a Tilde in a Path Not Expanded in a Shell Script
How to Add Users to Docker Container
How to Shutdown a Spring Boot Application in a Correct Way
What Happens If You Use the 32-Bit Int 0X80 Linux Abi in 64-Bit Code
How to Symlink a File in Linux
What Is Double Dot(..) and Single Dot(.) in Linux
How to Parse a CSV File in Bash
How to Know the Script File Name in a Bash Script
Separately Redirecting and Recombining Stderr/Stdout Without Losing Ordering