Using sed to copy one line to another - sed

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.

Related

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)

Delete lines by pattern in specific range of lines

I want to remove lines from file by regex pattern using sed just like in this question Delete lines in a text file that containing a specific string, but only inside a range of lines (not in the whole file). I want to do it starting from some line number till the end of file.
This is how I've done it in combination with tail:
tail -n +731 file|sed '/some_pattern/d' >> file
manually remove edited range in file from previous step
Is there a shorter way to do it with sed only?
Something like sed -i '731,1000/some_pattern/d' file?
You can use this sed,
sed -i.bak '731,1000{/some_pattern/d}' yourfile
Test:
$ cat a
1
2
3
13
23
4
5
$ sed '2,4{/3/d}' a
1
2
23
4
5
You need $ address to match end of file. With GNU sed:
sed -i '731,${/some_pattern/d;}' file
Note that this can be slower than tail -n +number, because sed will start processing at start of file instead of doing lseek() like tail.
(With BSD sed you need sed -i '' ...)
sed is for simple substitutions on individual lines, that is all. For anything even marginally more interesting an awk solution will be clearer, more robust, portable, maintainable, extensible and better in just about ever other desirable attribute of software.
Given this sample input file:
$ cat file
1
2
3
4
1
2
3
4
1
2
3
4
The following script will print every line except a line containing the number 3 that occurs after the 6th line of the input file:
$ awk '!(NR>6 && /3/)' file
1
2
3
4
1
2
4
1
2
4
Want to only do the deletion between lines 6 and 10? No problem:
$ awk '!(NR>6 && NR<10 && /3/)' file
1
2
3
4
1
2
4
1
2
3
4
Want the skipped lines written to a log file? No problem:
awk 'NR>6 && /3/{print > "log";next} {print}' file
Written to stderr?
awk 'NR>6 && /3/{print | "cat>&2";next} {print}' file
Want a count of how many lines you deleted also written to stderr?
awk 'NR>6 && /3/{print | "cat>&2"; cnt++; next} {print} END{print cnt | "cat>&2"}' file
ANYTHING you want to do additionally or differently will be easy and build on what you start with. Try doing any of the above, or just about anything else, with a sed script that satisfies your original requirement.
awk to the rescue!
awk '!(NR>=731 && /pattern/)' input > output

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

Replace multiple lines with 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