a=a
b=ab
c=''
d=''
echo ac | sed "s/$a/$b/;"
abc
echo ac | sed "s/$a/$b/; s/$c/$d/"
bc
Why does sed remove the character 'a' in the last example?
It happens because the $c is an empty string, and when sed is passed the subsequent s command with an empty pattern, the previous one is used, in this case, $a, which is a.
So,
echo ac | \
sed "s/$a/$b/; s/$c/$d/"
# a ab a
Here,
a is replaced with ab first with s/a/ab/ (ac turns into abc)
a is removed with s/a// (abc is turned into bc).
See documentation, the Regular Expressions in sed section:
If an RE is empty (that is, no pattern is specified) sed shall behave as if the last RE used in the last command applied (either as an address or as part of a substitute command) was specified.
Related
I am trying to find these two lines:
<paramsToUseForLimit></paramsToUseForLimit>
</hudson.plugins.throttleconcurrents.ThrottleJobProperty>
and replace them with:
<paramsToUseForLimit/>
</hudson.plugins.throttleconcurrents.ThrottleJobProperty>
<jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl plugin="branch-api#2.6.3">
<durationName>hour</durationName>
<count>2</count>
<userBoost>false</userBoost>
</jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl>
from my file config.json
Can someone help me to do this with sed ?
sed -ie "s/Those two lines/Replaced with those 7 lines/g" /config.json
sed may not be the best tool for this task.
sed -ie '/^ <\(paramsToUseForLimit>\)<\/\1/{
N
/\n<\/hudson.plugins.throttleconcurrents.ThrottleJobProperty>/{
i \
<paramsToUseForLimit/>\
</hudson.plugins.throttleconcurrents.ThrottleJobProperty>\
<jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl plugin="branch-api#2.6.3">\
<durationName>hour</durationName>\
<count>2</count>\
<userBoost>false</userBoost>\
</jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl>
d
}
}' /config.json
/^.../{ - if pattern space matches first match line:
N - append next line of input to pattern space
/\n.../{ - if pattern space matches second match line:
i \ - insert new text (\ before embedded newlines)
d- delete original text, implicit print, and start next cycle
otherwise, implicit print (two lines if N ran, else one line)
start next cycle
This is quite fragile. For example, watch out for shell metacharacters in the inserted text.
With GNU sed with -z option and inspired with Escape a string for a sed replace pattern question and answer - you can properly escape the patterns and replace newlines with \ n characters and then pass to sed. Then it's simple:
KEYWORD=' <paramsToUseForLimit></paramsToUseForLimit>
</hudson.plugins.throttleconcurrents.ThrottleJobProperty>'
REPLACE=' <paramsToUseForLimit/>
</hudson.plugins.throttleconcurrents.ThrottleJobProperty>
<jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl plugin="branch-api#2.6.3">
<durationName>hour</durationName>
<count>2</count>
<userBoost>false</userBoost>
</jenkins.branch.RateLimitBranchProperty_-JobPropertyImpl>'
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -z 's/[]\/$*.^[]/\\&/g; s/\n/\\n/g');
ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -z 's/[\/&]/\\&/g; s/\n/\\n/g')
sed -z "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/" input_file.txt
Let's say that I want to change all words ending with -et so that they instead end with -ert
Just doing a simple sed 's/et /ert /g' would work for all words followed by another word. But only if it is followed by a space. It can also be followed by '.', ',', ':' or other characters. Running sed for every individual case does not seem to be the correct way.
What I'm looking for is something like "for every match of AB, replace A with C, but don't touch B".
Clarification due to comments:
It's not limited to the ending of the words. I would like to do this inside words as well. Like:
$ echo "aaetxaa" | sed '?'
aaertxaa
$ echo "aaetyaa" | sed '?'
aaetyaa
So the replacement only took place when the matching ended with an x and not a y.
I assume that you want to match et only if it is followed with x or end of string.
In this case, you may use
sed -E 's/et(x|$)/ert\1/g'
See the online sed demo:
echo "aaetxaa" | sed -E 's/et(x|$)/ert\1/g'
# => aaertxaa
echo "aaetyaa" | sed -E 's/et(x|$)/ert\1/g'
# => aaetyaa
Details
-E - POSIX ERE syntax enabled
s/ - substitution command, / is the delimiter
et(x|$) - LHS: et followed with either x or end of string captured into Group 1
ert\1 - ert and the value of Group 1
g - global flag: match and replace all occurrences on a line.
I've read sed info. In "3.3 Overview of Regular Expression Syntax".
There is a description:
\digit
Matches the digit-th \(...\) parenthesized subexpression in the regular expression.
This is called a back reference. Subexpressions are implicitly numbered by
counting occurrences of \( left-to-right.
I don't know what it means. Who can give me a example?
Sure!
$ echo "23 45" | sed -r 's/^([0-9]*)/---\1---/'
---23--- 45
Graphically:
sed -r 's/^([0-9]*)/---\1---/'
# ^^^^^^^^ ^^
# capture -----|
# print back
As you see, in a sed expression on the form s/search/replace, you can "capture" a pattern in the search block and then print it back in the replace block by using \1, \2, ... The number is sequential and corresponds to the 1st, 2nd, ... group that has been captured.
$ echo "23 45" | sed -r 's/^([0-9]*) (.)/YEAH \2---\1---/'
YEAH 4---23---5
I have read sed info. In capture 3.5 :The s Command
There is a description:
The s command can be followed by zero or more of the following flags:
number
Only replace the numberth match of the regexp.
Note: the posix standard does not specify what should happen when you mix
the g and number modifiers, and currently there is no widely agreed
upon meaning across sed implementations. For GNU sed, the interaction
is defined to be: ignore matches before the numberth, and then match
and replace all matches from the numberth on.
I do not know how to use it ,who can give a example.
echo a1 | sed -n 's/\(a\)1/\13/p'
the result is no different with
echo a1 | sed -n 's/\(a\)1/\13/1p'
try this:
echo "hi hi hi" | sed 's/hi/hello/2'
echo "hi hi hi" | sed 's/hi/hello/3'
The number obviously only makes sense when there is more than one match.
sed 's/a/b/4' <<<aaaaa
aaaba
If there isn't a fourth match, obviously, no substitution takes place.
How can one replace a part of a line with sed?
The line
DBSERVERNAME xxx
should be replaced to:
DBSERVERNAME yyy
The value xxx can vary and there are two tabs between dbservername and the value. This name-value pair is one of many from a configuration file.
I tried with the following backreference:
echo "DBSERVERNAME xxx" | sed -rne 's/\(dbservername\)[[:blank:]]+\([[:alpha:]]+\)/\1 yyy/gip'
and that resulted in an error: invalid reference \1 on `s' command's RHS.
Whats wrong with the expression? Using GNU sed.
This works:
sed -rne 's/(dbservername)\s+\w+/\1 yyy/gip'
(When you use the -r option, you don't have to escape the parens.)
Bit of explanation:
-r is extended regular expressions - makes a difference to how the regex is written.
-n does not print unless specified - sed prints by default otherwise,
-e means what follows it is an expression. Let's break the expression down:
s/// is the command for search-replace, and what's between the first pair is the regex to match, and the second pair the replacement,
gip, which follows the search replace command; g means global, i.e., every match instead of just the first will be replaced in a line; i is case-insensitivity; p means print when done (remember the -n flag from earlier!),
The brackets represent a match part, which will come up later. So dbservername is the first match part,
\s is whitespace, + means one or more (vs *, zero or more) occurrences,
\w is a word, that is any letter, digit or underscore,
\1 is a special expression for GNU sed that prints the first bracketed match in the accompanying search.
Others have already mentioned the escaping of parentheses, but why do you need a back reference at all, if the first part of the line is constant?
You could simply do
sed -e 's/dbservername.*$/dbservername yyy/g'
You're escaping your ( and ). I'm pretty sure you don't need to do that. Try:
sed -rne 's/(dbservername)[[:blank:]]+\([[:alpha:]]+\)/\1 yyy/gip'
You shouldn't be escaping things when you use single quotes. ie.
echo "DBSERVERNAME xxx" | sed -rne 's/(dbservername[[:blank:]]+)([[:alpha:]]+)/\1 yyy/gip'
You shouldn't be escaping your parens. Try:
echo "DBSERVERNAME xxx" | sed -rne 's/(dbservername)[[:blank:]]+([[:alpha:]]+)/\1 yyy/gip'
This might work for you:
echo "DBSERVERNAME xxx" | sed 's/\S*$/yyy/'
DBSERVERNAME yyy
Try this
sed -re 's/DBSERVERNAME[ \t]*([^\S]+)/\yyy/ig' temp.txt
or this
awk '{if($1=="DBSERVERNAME") $2 ="YYY"} {print $0;}' temp.txt