Reputation: 6736
I'm developing a bash patch to perform multiple operations on plain text files to help reduce manual edits on multiple web servers. One part that is beyond the scope of my sed knowledge is how I would go about editing a string that occurs more than once, only editing one of the specific occurrences. Take the following redacted HTML for example:
<div class="dashlet">
<div class="body">
<div class="detail-list-item">
<!-- some content here -->
</div>
<div class="detail-list-item">
<!-- some more content here -->
</div>
<div class="detail-list-item">
<!-- some more content here -->
</div>
<div class="detail-list-item">
<!-- some more content here -->
</div>
<div class="detail-list-item last-item">
<!-- some final content here -->
</div>
</div>
</div>
I need to rid of the last block of code, and while not ideal given that this file may change in future updates, I'm using the following command to delete the content by lines
sed -i '29,33d' /path/to/file
Where 29 is the line that <div class="detail-list-item last-item">
is on, and 33 is it's corresponding closing </div>
tag. Is there a better way to do this to prevent future updated versions of this file so that I won't have to check the file to make sure I'm not deleting the wrong lines?
The final piece is that I need to replace the previous html class to include last-item
as a second class. So the final html will resemble:
<div class="dashlet">
<div class="body">
<div class="detail-list-item">
<!-- some content here -->
</div>
<div class="detail-list-item">
<!-- some more content here -->
</div>
<div class="detail-list-item">
<!-- some more content here -->
</div>
<div class="detail-list-item last-item">
<!-- some final content here -->
<!-- note how we are one div shorter and this div's class has a second class -->
</div>
</div>
</div>
What sed command(s) can accomplish this task?
Upvotes: 1
Views: 435
Reputation: 208505
Since sed processes the file line by line, it might not be the best solution for this. However since your file is pretty small, you can use this somewhat hacky solution which puts the entire file into the hold buffer, and then performs a substitution on the entire file at once:
sed -rni 'H;${x;s/\n(.*list-item)(".*)\n <div.* <\/div>/\1 last-item\2/p}' /path/to/file
Here is an explanation:
# options: -r extended regex so parentheses don't need to be escaped
# -n don't automatically print pattern space
# -i edit file in place
H; # add the current line to the hold space
$ # if we are at the last line
{ # commands until '}' are only run if at the last line
x; # swap pattern space and hold space
s/ # search/replace
\n(.*list-item) # greedy match to the last 'list-item', put in group 1
(".*) # double quote must follow 'list-item', match as many
# characters as possible and put in group 2
\n <div.* <\/div> # match all of the next (final) double-indented
# div, don't put it in a group
/
\1 last-item\2 # insert ' last-item' before groups 1 and 2, final
# double-indented div will be deleted
/p # print the result
}
You could do the part where you delete the final div with a much simpler command:
sed -i '/<div.*last-item/,/<\/div>/d' /path/to/file
Unfortunately I am not aware of a simple way to add last-item
as a second class to the final div.
Upvotes: 2