I’m looking for a way to only execute replacement when the last character is a newline, using sed.
For instance:
lettersAtEndOfLine
is replaced, but this is not:
lettersWithCharacterAfter&
Since sed does not work well with newlines, it is not as simple as
$ sed -E "s/[a-zA-Z]*n/replace/" file.txt
How can this be accomplished?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
With standard sed, you will never see a newline in the text read from a file. This is because sed reads line by line, and there is therefore no newline at the end of the text of the current line in sed‘s pattern space. In other words, sed reads newline-delimited data, and the delimiters are not part of what a sed script sees.
Regular expressions can be anchored at the end of the line using $ (or at the beginning, using ^). Anchoring an expression at the start/end of a line forces it to match exactly there, and not just anywhere on the line.
If you want to replace anything matching the pattern [A-Za-z]* at the end of the line with something, then anchoring the pattern like this:
[A-Za-z]*$
…will force it to match at the end of the line and nowhere else.
However, since [A-Za-z]*$ also matches nothing (for example, the empty string present at the end of every line), you need to force the matching of something, e.g. by specifying
[A-Za-z][A-Za-z]*$
or
[A-Za-z]{1,}$
So, your sed command line will thus be
$ sed 's/[A-Za-z]{1,}$/replace/' file.txt
I did not use the -E switch here because it’s not needed. With it, you could have written
$ sed -E 's/[A-Za-z]+$/replace/' file.txt
It’s a matter of taste.
Method 2
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt
Or, the long complex unnecessary way:
I’ve found out, this can be done, still using sed, with the help of
tr. You can assign another character to represent the end of the line.
Another temporary character has to be used, in this case “`”. Let’s
use “~” to represent the end of the line:tr 'n' '`' <input.txt >output.txt sed -i "s/`/~`/" output.txt tr '`' 'n' <output.txt >result.txtAnd then to perform the actual search and replace, use “~” rather than
“n”:sed -i -E "s/[a-zA-Z]*~/replace/" result.txtAnd then clean up the extra character on the other lines:
sed -i "s/~//" result.txtObviously, this can all be piped together resulting in something like:
tr 'n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' 'n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt
Method 3
From the (broken) code snippet you posted, you seem to want to replace the newline as well. In that case, regex anchoring by itself can’t help you. The following is a solution:
sed '/[[:alpha:]]+$/{N;s/[[:alpha:]]+n/replace/}' your_file
Broken down:
/[a-zA-Z]+$/{}means apply whatever comes inside the curlies to lines that match the regex.- The regex is the one that uses anchoring as seen in your own answer, modified to take glenn jackman’s comments into account.
- Inside the curlies,
Nmeans “append the next line to the active buffer” (whatsedcalls the ‘pattern space’) - Finally the
s///statement is your required substitution. It now works because the pattern space contains two successive lines and the newline is therefore a part of it.
Method 4
To find the end of line, just use the $-sign:
Without end of line anchor:
sed -n '/pattern/p' file
Without end of line anchor:
sed -n '/pattern$/p' file
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0