Replace multiple lines with sed - sed

I have in sample.txt the following content
abc
efg
hij
klm
nop
qrs
I have tried replacing abc with other text with
sed -i '/abc/c\This line is removed by the admin.' sample.txt
Output:
This line is removed by the admin.
efg
hij
klm
nop
qrs
It worked but for a single line.
But I am wondering how could I replace a given set of lines say 1 to 3 using sed?

If you know the line numbers, you prepend them to your pattern, like so:
sed -i '4 s/abc/c\This line is removed by the admin./' sample.txt
The above will change line 4. If you want to change ranges (say, lines 5-10), enter the start and end line numbers with a comma between:
sed -i '5,10 s/abc/c\This line is removed by the admin./' sample.txt
$ represents the last line in the file so if you wanted say, line 100 to the end:
sed -i '100,$ s/abc/c\This line is removed by the admin./' sample.txt
You may find this link helpful. Look for the section on Ranges by line number.

If your only criterion is the line numbers, then you can specify them like so:
sed -i '1,3 s/.*/This line is removed by the admin./' sample.txt

Here is an awk solution if you like to try:
awk 'NR>=1 && NR<=3 {$0="This line is removed by the admin."}1' file
This line is removed by the admin.
This line is removed by the admin.
This line is removed by the admin.
klm
nop
qrs
To write it back to the file
awk 'NR>=1 && NR<=3 {$0="This line is removed by the admin."}1' file > tmp && mv tmp file

This might work for you (GNU sed):
sed '1,3c\replace lines 1 to 3 with this single line' file
if you want to replace each line within the range use:
sed $'1,3{\\athis replaces the original line\nd}' file
or perhaps more easily:
sed '1,3s/.*/this replaces the original line/' file

Related

Using sed to copy one line to another

I need to replace a line (4) with a copy of another line (6) in a range of files.
So far I know how to return a single line (although returns with carriage return?)...
sed -n '6p' *
also this doesn't work with * for files, only seems to return the first file.
And I can also replace a line with some chars...
sed -i '5s/.*/ 00/' *
But I cannot figure out how to do both together.
Edit: One step closer but now need to apply to multiple files (in the same folder). * reads in the first file only.
sed -i '4s/.*/sed -n '6p' file.nc/e' file.nc
This might work for you (GNU sed):
sed -Ei '4{:a;N;6!ba;s/^[^\n]*(\n.*\n(.*))/\2\1/}' file1 file2 file3 fileetc
Gather up lines between line 4 and 6 and then replace line 4 by line 6.
Alternative:
sed '4d;5,6{H;5h;6!d;G}' file
Delete line 4 (not needed).
In the range between lines 5 and 6, append a copy of line 6 to a copy of line 5 and append those copies to line 6.

How to delete line with specific line numbers and save deleted and the rest?

test.txt contains:
this is a line
another line
one more line
For example, this can remove 1-2 lines, and save the rest into the rest.txt
sed -e '1,2d' test.txt > rest.txt
But the original file remains intact. Then how to get the remaining lines from the file? In this example, I want to remove the first 2 lines, save them into a file 'deleted.txt', and save the 3rd line into 'rest.txt'.
With GNU sed:
seq 1 5 | sed -e '1,2w deleted.txt' -e '1,2d' > rest.txt
w filename: Write the current pattern space to filename.
awk 'NR<=2{print $0 > "deleted.txt"}NR>2{print $0 > "rest.txt"}' test.txt
For lines(here NR) <= 2 redirect the output to deleted.
For lines(here NR) > 2 redirect the output to rest.txt.

sed: get a line number with regex and insert text at that line

I want to get the first line of a file that is not commented out with an hash, then append a line of text just after that line just before that line.
I managed to get the number of the line:
sed -n '/^\s*#/!{=;q}' file // prints 2
and also to insert text (specifying the line manually):
sed '2 a extralinecontent' file
I can't get them working together as a one liner or in a batch.
I tried command substitution (with $(command) and also with backticks) but I get an error from bash:
sed '$(sed -n '/^\s*#/!{=;q}' file) a extralinecontent' file
-bash: !{=: event not found
and also tried many other combinations, but no luck.
I'm using gnu-sed (via brew) on macOS.
This might work for you (GNU sed):
sed -e '/^\s*#/b;a extra line content' -e ':a;n;ba' file
Bail out of any lines beginning with a comment at the beginning of the file, append an extra line following the first line that is not a comment and keep fetching/printing all the remaining lines of the file.
Here's a way to do it with GNU sed without reading the file twice
$ cat ip.txt
#comment
foo baz good
123 456 7889
$ sed -e '0,/^\s*[^#[:space:]]/ {// a XYZ' -e '}' ip.txt
#comment
foo baz good
XYZ
123 456 7889
GNU sed allows first address to be 0 if the other address is regex, that way this will work even if first line matches the condition
/^\s*[^#[:space:]]/ as sed doesn't support possessive quantifier, need to ensure that the first character being matched by the character class isn't either a # or a whitespace character
// is a handy shortcut to repeat the last regex
a XYZ your required line to be appended (note that your question mentiones insert, so if you want that, use i instead of a)

Using sed to keep the beginning of a line

I have a file in which some lines start by a >
For these lines, and only these ones, I want to keep the first eleven characters.
How can I do that using sed ?
Or maybe something else is better ?
Thanks !
Muriel
Let's start with this test file:
$ cat file
line one with something or other
>1234567890abc
other line in file
To keep only the first 11 characters of lines starting with > while keeping all other lines:
$ sed -r '/^>/ s/(.{11}).*/\1/' file
line one with something or other
>1234567890
other line in file
To keep only the first eleven characters of lines starting with > and deleting all other lines:
$ sed -rn '/^>/ s/(.{11}).*/\1/p' file
>1234567890
The above was tested with GNU sed. For BSD sed, replace the -r option with -E.
Explanation:
/^>/ is a condition. It means that the command which follows only applies to lines that start with >
s/(.{11}).*/\1/ is a substitution command. It replaces the whole line with just the first eleven characters.
-r turns on extended regular expression format, eliminating the need for some escape characters.
-n turns off automatic printing. With -n in effect, lines are only printed if we explicitly ask them to be printed. In the second case above, that is done by adding a p after the substitute command.
Other forms:
$ sed -r 's/(>.{10}).*/\1/' file
line one with something or other
>1234567890
other line in file
And:
$ sed -rn 's/(>.{10}).*/\1/p' file
>1234567890

sed - insert lines when text found / not found

I have issue with sed, i need to accomplish two things with a csv file
in front of each line that does not start UNES I need to add tag "BF2;"
at the start of the file (after UNES if present) I need to add a tag "UNH;"
Example (no UNES;)
50000024;IE15;041111;113901;verstuurd;Aangift;
50000024;IE15;041111;113901;verstuurd;Aangifte;
50000024;IE15;041111;113901;verstuurd;Aangifte;
Example (with UNES;)
UNES;
50000024;IE15;041111;113901;verstuurd;Aangift;
50000024;IE15;041111;113901;verstuurd;Aangifte;
50000024;IE15;041111;113901;verstuurd;Aangifte;
so far I have this:
sed -e 's/^\([^"UNES"]\)/BF2;\1/' | sed '/UNES/ a\UNH;'
THis works as long as a UNES; tag is present - I can't seem to figure out how to insert the UNH; when UNES is not present!
Any help much appreciated
Sample output:
UNES;
UNH;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
Here's how you could do it using awk:
awk 'NR==1 {if(f=/^UNES;/)print; print "UNH;"} !f{print "BF2;" $0} {f=0}' file
On the first line, if /^UNES;/ is matched, print it and set the flag f. Always print "UNH;". If the f flag has been set, don't do the next action, which works for the rest of the lines. Always reset f to 0 after the first line so all further lines have "BF2;" added to the start.
Testing it out:
$ cat file
UNES;
50000024;IE15;041111;113901;verstuurd;Aangift;
50000024;IE15;041111;113901;verstuurd;Aangifte;
50000024;IE15;041111;113901;verstuurd;Aangifte;
$ awk 'NR==1 {if(f=/^UNES;/)print; print "UNH;"} !f{print "BF2;" $0} {f=0}' file
UNES;
UNH;
BF2;50000024;IE15;041111;113901;verstuurd;Aangift;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
$ cat file2
50000024;IE15;041111;113901;verstuurd;Aangift;
50000024;IE15;041111;113901;verstuurd;Aangifte;
50000024;IE15;041111;113901;verstuurd;Aangifte;
$ awk 'NR==1 {if(f=/^UNES;/)print; print "UNH;"} !f{print "BF2;" $0} {f=0}' file2
UNH;
BF2;50000024;IE15;041111;113901;verstuurd;Aangift;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
BF2;50000024;IE15;041111;113901;verstuurd;Aangifte;
You can use this sed command:
sed '/^UNES;$/{i\
UNH;
n};s/^/BF2;/;' file.txt
details:
/^UNES;$/i\
UNH; insert a new line when UNES; is the whole line.
n replaces the pattern space with the next line
Try this, its works for me
sed '/^UNES;$/{i\
UNH;
n};s/^[0-9]*/BF2;&/;'