Sed delimiter options - sed

In my code, I need to replace variable assignments with addresses:
sed -i "s/^variable = .*$/variable = http://myaddress/"
Obviously, this does not work because the forward slashes in the address are recognized in the sed command.
I want to keep the $ at the end of the first expression for replacing anything to the end of the line. I also do not want to escape the dollar sign as such, \$ because it will search for a dollar sign.
Also, I don't want to just escape the forward slashes in the address as there are also variables in some places for the addresses.
I've tried using # instead of / but have to include what I don't want to - the \$.
Are there any alternate delimiters I can use that fit my situation?

The $ is interpreted by your shell. Wrap the whole argument to sed with ' to prevent this.
sed -i 's#^variable = .*$#variable = http://myaddress#'

sed -i "s#^variable = .*$#variable = http://myaddress#" file
should work for you.
Note that the $ in the first expression is not literature "dollar", but a regex expression, means, the end of the line.

sed -i 's#^variable = .*#variable = http://myaddress#'

Related

What do I miss in this sed Expression?

I'd like to replace the database server of a horde config file from "localhost" to a remote server (I use "database.contoso.com" as a placeholder).
The file in question is /var/www/horde/config/conf.php.
The line in the file looks like this:
$conf['sql']['hostspec'] = 'localhost';
Now I have created a sed line like so:
sed s/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'localhost\'\;/\$conf\[\'sql\'\]\[\
'hostspec\'\]\ \=\ \'database\.contoso\.com\'\;/ /var/www/horde/config/conf.php
But for whatever reason, it does not work -I spare out the -i option for later.
While trying to figure out, why it does not work, I did this:
echo "\$conf['sql']['hostspec'] = 'localhost';"|sed s/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'localhost\'\;/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'database\.contoso\.com\'\;/
which returns this:
$conf['sql']['hostspec'] = 'localhost';
but it should return:
$conf['sql']['hostspec'] = 'database.contoso.com';
What am I missing?
From Escape a string for a sed replace pattern in this case it would work:
KEYWORD="\$conf['sql']['hostspec'] = 'localhost';"
REPLACE="\$conf['sql']['hostspec'] = 'database.contoso.com';"
ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/"
The immediate problem is that you are not quoting enough. To match a regex metacharacter literally, you need to pass in a literal backslash \\ followed by a literal, like for example \[. But the simplest solution by far is to use single quotes around your expression, and then only backslash the characters which are regex metacharacters.
Literal single quotes inside single quotes are still challenging. Here, I have chosen to end the single-quoted string, insert a backslash-escaped but otherwise unquoted single quote, and add an opening single quote to continue with another single-quoted string. The shell glues these together into a single string.
echo "\$conf['sql']['hostspec'] = 'localhost';" |
sed 's/\$conf\['\''sql'\''\]\['\''hostspec'\''\] = '\''localhost'\'';/$conf['\''sql'\'']['\''hostspec'\''] = '\''database.contoso.com'\'';/'
A better solution generally is to use backreferences to quote back part of the matched string so you don't have to repeat it.
echo "\$conf['sql']['hostspec'] = 'localhost';" |
sed 's/\(\$conf\['\''sql'\''\]\['\''hostspec'\''\] = '\''\)[^'\'']*'\'';/\1database.contoso.com'\'';/'
Demo: https://ideone.com/RA0MSi
A much much much better solution is to change your PHP script so that this setting can be overridden with an option, an environment variable, and/or a configuration file.
This might work for you (GNU sed & shell):
sed -E 's/(\$conf\[('\'')sql\2]\[\2hostspec\2\] = )\2localhost\2;/\1\2database.contoso.com\2;/' file
Use pattern matching to match and replace.
N.B. Certain metacharacters must be escaped/quoted i.e. $,[,] and then because the sed commands are surrounded by single quotes, each single quote (within the substitution command) must be replaced by '\'' (see here for reasoning). Also, back references can be used both in the RHS and the LHS of the substitution command. The back references in the LHS especially allow for the shortening of the overall command and perhaps make the regexp more readable.

Comment out one line of many files with sed

I need to comment out a line of many files on one path. The line reads
input_types = ['text']
and I need to replace it by
#input_types = ['text']
I want to do something like
sed -i 's/input_types/#input_types/g' path/to/files/*
but that would change all instances of input_types and I don't want that.
I also tried
sed -i 's/input_types = ['text']/#input_types = ['text']/g' path/to/files/*
but it didn't work
How can I change only that specific instance?
You last try was quite good, but two things have to be changed:
You use single quotes to enclose your expression, but single quotes are also part of the expression -- that gets confusing. In this case it's better to use double quotes for enclosing the expression, instead.
The [ ] brackets have to be escaped with backslashes: \[ \]
So, if you change the line to
sed -i "s/input_types = \['text'\]/#input_types = \['text'\]/g" /path/to/files/*
it should work.

sed command not working properly on ubuntu

I have one file named `config_3_setConfigPW.ldif? containing the following line:
{pass}
on terminal, I used following commands
SLAPPASSWD=Pwd&0011
sed -i "s#{pass}#$SLAPPASSWD#" config_3_setConfigPW.ldif
It should replace {pass} to Pwd&0011 but it generates Pwd{pass}0011.
The reason is that the SLAPPASSWD shell variable is expanded before sed sees it. So sed sees:
sed -i "s#{pass}#Pwd&0011#" config_3_setConfigPW.ldif
When an "&" is on the right hand side of a pattern it means "copy the matched input", and in your case the matched input is "{pass}".
The real problem is that you would have to escape all the special characters that might arise in SLAPPASSWD, to prevent sed doing this. For example, if you had character "#" in the password, sed would think it was the end of the substitute command, and give a syntax error.
Because of this, I wouldn't use sed for this. You could try gawk or perl?
eg, this will print out the modified file in awk (though it still assumes that SLAPPASSWD contains no " character
awk -F \{pass\} ' { print $1"'${SLAPPASSWD}'"$2 } ' config_3_setConfigPW.ldif
That's because$SLAPPASSWD contains the character sequences & which is a metacharacter used by sed and evaluates to the matched text in the s command. Meaning:
sed 's/{pass}/match: &/' <<< '{pass}'
would give you:
match: {pass}
A time ago I've asked this question: "Is it possible to escape regex metacharacters reliably with sed". Answers there show how to reliably escape the password before using it as the replacement part:
pwd="Pwd&0011"
pwdEscaped="$(sed 's/[&/\]/\\&/g' <<< "$pwd")"
# Now you can safely pass $pwd to sed
sed -i "s/{pass}/$pwdEscaped/" config_3_setConfigPW.ldif
Bear in mind that sed NEVER operates on strings. The thing sed searches for is a regexp and the thing it replaces it with is string-like but has some metacharacters you need to be aware of, e.g. & or \<number>, and all of it needs to avoid using the sed delimiters, / typically.
If you want to operate on strings you need to use awk:
awk -v old="{pass}" -v new="$SLAPPASSWD" 's=index($0,old){ $0 = substr($0,1,s-1) new substr($0,s+length(old))} 1' file
Even the above would need tweaked if old or new contained escape characters.

Sed replace text

I need to replace text with single quotes with sed and I can't get it to work. Here is my code; can you help me?
I have a text file with this format:
#sometext
$configuration_TEstbk2_bk2_environment12 = 'lalala'
$configuration_TEstbk2_bk2_envoronment12 = 'lalala1'
$configuration_TEstbk2_bk2_staging12 = 'BACKUP 2'
$configuration_waq4faw4f_q4fq4qg4f = 'r234rq43rq4rqr'
$configuration_alice_StagingTEstBk_bk = 'testebk'
$configuration_deployment_overlays_alice_TEStStngDir = 'some'
$configuration_arefgqrqgrq_341q34tq34t = '134t135'
And I need to do something like:
sed s/$configuration_arefgqrqgrq_341q34tq34t ='134t135'/$configuration_arefgqrqgrq_341q34tq34t = 'NEWVALUE'/g
I have tried with many combinations with sed but I can't find one that works.
Would this work for you?
sed "/\$config_deployment_overlays_alice_arefgqrqgrq_341q34tq34t_staging/s/'134t135'/'NEWVALUE'/" file
I'd probably use:
sed '/$configuration_arefgqrqgrq_341q34tq34t *=/'"s/'134t135'/'NEWVALUE'/"
This uses a mix of single quotes and double quotes at the shell level to get the correct information to sed. The single quotes enclose the search condition for the lines containing $configuration_arefgqrqgrq_341q34tq34t followed by some blanks and an equals sign. This avoids the shell expanding what might be a shell variable name (but probably isn't, so the empty string would be substituted). I then switch to double quotes at the shell level, so that sed gets to see the single quotes: it substitutes the value in single quotes with the replacement value, but only on those lines that contain the given configuration parameter name.
I hope users never have to type those configuration parameter names.
I suppose your problem is with the quoting. You could use complex quoting to make sure everything is in single quotes, except the single quotes which need to be in double quotes:
sed 's/$configuration_arefgqrqgrq_341q34tq34t *= *'"'134t135'"'/$configuration_arefgqrqgrq_341q34tq34t = '"'NEWVALUE'/g"
... or you could use some temporary variables to make the whole thing more readable, and suitable for inclusion in a single pair of double quotes:
directive='$configuration_arefgqrqgrq_341q34tq34t'
oldvalue="'134t135'"
newvalue="'NEWVALUE'"
sed "s/$directive *= *$oldvalue/$directive = $newvalue/g"
(If you only expect one match, the /g flag is superfluous.)
You can also totally avoid matching quotes by capturing them:
sed '/$configuration_arefgqrqgrq_341q34tq34t/{
s/\(= *.\).*\(.\) *$/\1NEWVALUE\2/
}' input
This might work for you (GNU sed):
sed -r 's/^(\$\S+\s=\s'\'').*('\'')/\1NEWVALUE\2/' file

Confining Substitution to Match Space Using sed?

Is there a way to substitute only within the match space using sed?
I.e. given the following line, is there a way to substitute only the "." chars that are contained within the matching single quotes and protect the "." chars that are not enclosed by single quotes?
Input:
'ECJ-4YF1H10.6Z' ! 'CAP' ! '10.0uF' ! 'TOL' ; MGCDC1008.S1 MGCDC1009.A2
Desired result:
'ECJ-4YF1H10-6Z' ! 'CAP' ! '10_0uF' ! 'TOL' ; MGCDC1008.S1 MGCDC1009.A2
Or is this just a job to which perl or awk might be better suited?
Thanks for your help,
Mark
Give the following a try which uses the divide-and-conquer technique:
sed "s/\('[^']*'\)/\n&\n/g;s/\(\n'[^.]*\)\.\([^']*Z'\)/\1-\2/g;s/\(\n'[^.]*\)\.\([^']*uF'\)/\1_\2/g;s/\n//g" inputfile
Explanation:
s/\('[^']*'\)/\n&\n/g - Add newlines before and after each pair of single quotes with their contents
s/\(\n'[^.]*\)\.\([^']*Z'\)/\1-\2/g - Using a newline and the single quotes to key on, replace the dot with a dash for strings that end in "Z"
s/\(\n'[^.]*\)\.\([^']*uF'\)/\1_\2/g - Using a newline and the single quotes to key on, replace the dot with a dash for strings that end in "uF"
s/\n//g - Remove the newlines added in the first step
You can restrict the command to acting only on certain lines:
sed "/foo/{s/\('[^']*'\)/\n&\n/g;s/\(\n'[^.]*\)\.\([^']*Z'\)/\1-\2/g;s/\(\n'[^.]*\)\.\([^']*uF'\)/\1_\2/g;s/\n//g}" inputfile
where you would substitute some regex in place of "foo".
Some versions of sed like to be spoon fed (instead of semicolons between commands, use -e):
sed -e "/foo/{s/\('[^']*'\)/\n&\n/g" -e "s/\(\n'[^.]*\)\.\([^']*Z'\)/\1-\2/g" -e "s/\(\n'[^.]*\)\.\([^']*uF'\)/\1_\2/g" -e "s/\n//g}" inputfile
$ cat phoo1234567_sedFix.sed
#! /bin/sed -f
/'[0-9][0-9]\.[0-9][a-zA-Z][a-zA-Z]'/s/'\([0-9][0-9]\)\.\([0-9][a-zA-Z][a-zA-Z]\)'/\1_\2/
This answers your specific question. If the pattern you need to fix isn't always like the example you provided, they you'll need multiple copies of this line, with reg-expressions modified to match your new change targets.
Note that the cmd is in 2 parts, "/'[0-9][0-9].[0-9][a-zA-Z][a-zA-Z]'/" says, must match lines with this pattern, while the trailing "s/'([0-9][0-9]).([0-9][a-zA-Z][a-zA-Z])'/\1_\2/", is the part that does the substitution. You can add a 'g' after the final '/' to make this substitution happen on all instances of this pattern in each line.
The \(\) pairs in match pattern get converted into the numbered buffers on the substitution side of the command (i.e. \1 \2). This is what gives sed power that awk doesn't have.
If your going to do much of this kind of work, I highly recommend O'Rielly's Sed And Awk book. The time spent going thru how sed works will be paid back many times.
I hope this helps.
P.S. as you appear to be a new user, if you get an answer that helps you please remember to mark it as accepted, or give it a + (or -) as a useful answer.
this is a job most suitable for awk or any language that supports breaking/splitting strings.
IMO, using sed for this task, which is regex based , while doable, is difficult to read and debug, hence not the most appropriate tool for the job. No offense to sed fanatics.
awk '{
for(i=1;i<=NF;i++) {
if ($i ~ /\047/ ){
gsub(".","_",$i)
}
}
}1' file
The above says for each field (field seperator by default is white space), check to see if there is a single quote, and if there is , substitute the "." to "_". This method is simple and doesn't need complicated regex.