Reputation:
Do you know of a better or easier way of using sed or Bash to replace from the last occurrence of a regex match from the end of a line without using rev
?
Here is the rev
way to match the third from last occurrence of the letter 's' – still forward matching, yet utilizing rev
to match from the last character.
echo "a declaration of sovereignty need no witness" | rev | sed 's/s/S/3' | rev
a declaration of Sovereignty need no witness
Update -- a generalized solution based on Cyruses answer:
SEARCH="one"
OCCURRENCE=3
REPLACE="FOUR"
SED_DELIM=$'\001'
SEARCHNEG=$(sed 's/./[^&]*/g' <<< "${SEARCH}")
sed -r "s${SED_DELIM}${SEARCH}((${SEARCHNEG}${SEARCH}){$((${OCCURRENCE}-1))}${SEARCHNEG})\$${SED_DELIM}${REPLACE}\1${SED_DELIM}" <<< "one one two two one one three three one one"
Note: To be truly generic it should escape regex items from the LHS.
Upvotes: 1
Views: 675
Reputation: 241858
In Perl, you can use a look-ahead assertion:
echo "a declaration of sovereignty need no witness" \
| perl -pe 's/s(?=(?:[^s]*s[^s]*){2}$)/S/'
(?=...)
is the look-ahead assertion, it means "if followed by ...", but the ... part is not matched and therefore not substituted[^s]*s[^s]*
means there's only one s
wrapped possibly by non-s
es{2}
is the number of repetitions, i.e. we want two s'es after the one we want to replaceUpvotes: 0
Reputation: 20002
You first need to count the number of occurances and figure out which one to replace:
echo "importantword1 importantword2 importantword3 importantword4 importantword5 importantword6" |
grep -o "importantword" | wc -l
Use that for your sed string
echo "importantword1 importantword2 importantword3 importantword4 importantword5 importantword6" |
sed 's/\(\(.*importantword.*\)\{3\}\)importantword\(\(.*importantword.*\)\{2\}\)/\1ExportAntword\3/'
importantword1 importantword2 importantword3 ExportAntword4 importantword5 importantword6
You get the same answer with setting variables for left and right:
l=3
r=2
echo "importantword1 importantword2 importantword3 importantword4 importantword5 importantword6" |
sed 's/\(\(.*importantword.*\)\{'$l'\}\)importantword\(\(.*importantword.*\)\{'$r'\}\)/\1ExportAntword\3/'
or
str="\(.*importantword.*\)"
echo "importantword1 importantword2 importantword3 importantword4 importantword5 importantword6" |
sed 's/\('${str}'\{'$l'\}\)importantword\('${str}'\{'$r'\}\)/\1ExportAntword\3/'
Upvotes: 0
Reputation: 785146
Using awk:
str='a declaration of sovereignty need no witness'
awk -v r='S' -v n=3 'BEGIN{FS=OFS="s"} {
for (i=1; i<=NF; i++) printf "%s%s", $i, (i<NF)?(i==NF-n?r:OFS):ORS}' <<< "$str"
Output:
a declaration of Sovereignty need no witness
Upvotes: 0
Reputation: 88601
With GNU sed:
sed -r 's/s(([^s]*s){2}[^s]*)$/S\1/' file
Output:
a declaration of Sovereignty need no witness
See: The Stack Overflow Regular Expressions FAQ
Upvotes: 2