Search for a String, and retrieve the line having it and all lines following it until another specific pattern - linux

Using linux, I want to search a text file for the string Blah and then return the line full line that contained the string and all the lines following the pattern up until a line that contains the word Failed.
For example,
Test Case Name "Blah"
Error 1
Error 2
Error 3
Failed
Test Case Name "Foo"
Pass
Test Case Name "Red"
Pass
In the above, I want to search for "Blah", and then return:
Test Case Name "Blah"
Error 1
Error 2
Error 3
Up until the line Failed. There can be any number of "Error" lines between Blah and Failed.
Follow up to make it faster
Both sed and awk options worked.
sed '/Blah/!d;:a;n;/Failed/d;ba' file
and
awk '/Failed/{p=0}/Blah/{p=1}p;' file
However, I noticed that while returning the expected outcome is quite fast, it takes ages to exit. Maybe these commands are recurrently searching for Blah and given that it appears only once, they run until the end-of-file.
This would not be much of a problem but I'm working with a file that contains 10 million lines and for now it is painfully slowly.
Any suggestions on how to exit after finding both lines containing Blah and Failed would be much appreciated.
Thanks!

With sed:
sed '/Blah/,/Failed/!d;//{1!d;}' file
/Blah/: match lines from Blahto Failed
!d: do not delete previous matching lines
//{1!d;}: from lines matching the addresses (that is Blahand Failed), do not delete the first one 1!d.

This might work for you (GNU sed):
sed -n '/Blah/,/Failed/{/Failed/!p}' file
Print the lines between and including Blah to Failed unless the line contains Failed.
sed ':a;/Blah/!d;:b;n;/Failed/ba;bb' file
If a line does not contain Blah delete it. Otherwise, print the current line and fetch the next (n). If this line contains Failed delete it and begin next iteration. Otherwise, repeat until successful or end-of-file.
The first solution prevents Blah and Failed being printed if they inhabit the same line. The second alternative, allows this.

would you like do with awk?
awk '/Failed/{p=0}/Blah/{p=1}p;' file will works for you.

Related

How to use m with the ed function in a Bash Script [duplicate]

I just need to move a line up in sed. I can select the line with
sed -i '7s///'
I need to move line 7 up 2 lines so it will be line 5.
I can't find anything on the internet to do this without complicated scripts, I can't find a simple solution of moving a specific line a specific number of times.
seq 10|sed '5{N;h;d};7G'
when up to line 5 append next line(line 6) into pattern space then save them into hold space and delete them from pattern space; up to line 7 then append the hold space content("5\n6") behind the line 7; now, pattern space is "7\n5\n6";finally,sed will print the pattern space at the end of current cycle by default(if no "-n" parameter)
ed is better at this, since it has a "move" command that does exactly what you want. To move line 7 to be the line after line 4, just do 7m4. ed doesn't write the data back by default, so you need to explicitly issue a w command to write the data:
printf '7m4\nw\n' | ed input
Although it is perhaps better to use a more modern tool:
ex -s -c 7m4 -c w -c q input

How to renumber lines using a vi command?

I am trying to renumber the lines 2,$ in a file using vim a command, I know the command cat -n of nl, I can number the lines, but I didn't get the expected output:
I tried this :2,$s/^\([^,]\)// | 2,$!cat -n
input:
#,Name,Types,Total,HP,Attack,Weaknesses,Strength
493,Arceus,Normal,720,120,120,Fighting,strong
483,Dialga,Steel;Dragon,680,100,120,Fighting;Ground,strong
250,Ho-oh,Fire;Flying,680,106,130,Electric;Water;Rock,strong
.... moer 100 lines
expected output:
#,Name,Types,Total,HP,Attack,Weaknesses,Strength
1,Arceus,Normal,720,120,120,Fighting,strong
2,Dialga,Steel;Dragon,680,100,120,Fighting;Ground,strong
3,Ho-oh,Fire;Flying,680,106,130,Electric;Water;Rock,strong
....
You can use \= to use a sub-replace-expression, and line('.') to get the current line number:
" The parenthesis around `line('.')-1` are not needed, but it seems clearer to me
:2,$s/^/\=(line('.')-1).','/
Edit: just realized you're actually replacing your first column, so you might actually want
:2,$s/^\d\+/\=line('.')-1/

2g is not working in sed to skip the first occurrence of a match

I am trying to replace some text in a xml file using sed. I am able to replace my text, but i want to skip the first occurence. i am using 2g, but it is not working. No error is displayed, but no change happens to file.
My Xml file :
<file-min-size>10830</file-min-size>
<rotate-log>true</rotate-log>
<file-min-size>25600</file-min-size>
<rotate-log>true</rotate-log>
<file-min-size>32300</file-min-size>
<rotate-log>true</rotate-log>
<file-min-size>13456</file-min-size>
<rotate-log>true</rotate-log>
My expected output :
<file-min-size>10830</file-min-size>
<rotate-log>true</rotate-log>
<file-min-size>25600</file-min-size>
<rotate-log>true insertvalue</rotate-log>
<file-min-size>32300</file-min-size>
<rotate-log>true insertvalue</rotate-log>
<file-min-size>13456</file-min-size>
<rotate-log>true insertvalue</rotate-log>
I am using the below sed command.
sed -i 's#</rotate-log>#insertvalue</rotate-log>#2g' myfile.xml
The above command is not working. if i remove 2g, then the text is repalcing. i want to skip the first occurence. Any help ?
Also when i run the command second time, the values are entering again. Is there a way to check and replace only if not available ?
With GNU sed, you may use
sed -i '/<\/rotate-log>/{:A;n;s#</rotate-log># insertvalue</rotate-log>#;bA}' file
See the online sed demo
The command finds the line with </rotate-log> and then
:A - sets a label A
n - discards the current pattern space value and reads the next line into it
s#</rotate-log># insertvalue</rotate-log># - replaces </rotate-log> with # insertvalue</rotate-log>
bA - goes to A label (reads the next line, replaces, goes on).

How to delete a particular line which has occurred many times in the file in vim

I want to delete a line: This is an example, which occurs multiple times in a file. How do I go about it.
Thanks,
Alisha
You could do:
:g/This is an example/d
:%s/This is an example\n//gc
% indicates all lines of a file
s indicates pattern to be searched.
g for global replacement
c for confirmation on each replace
If you want to delete the lines containing only the exact match you could:
:g/^This is an example$/d
You can do this using an external command:
:%!grep -v "This is an example"
This filters the entire file through the given command. The grep -v command selects all the lines of the file that do not match the given regular expression.

How can I replace a specific line by line number in a text file?

I have a 2GB text file on my linux box that I'm trying to import into my database.
The problem I'm having is that the script that is processing this rdf file is choking on one line:
mismatched tag at line 25462599, column 2, byte 1455502679:
<link r:resource="http://www.epuron.de/"/>
<link r:resource="http://www.oekoworld.com/"/>
</Topic>
=^
I want to replace the </Topic> with </Line>. I can't do a search/replace on all lines but I do have the line number so I'm hoping theres some easy way to just replace that one line with the new text.
Any ideas/suggestions?
sed -i yourfile.xml -e '25462599s!</Topic>!</Line>!'
sed -i '25462599 s|</Topic>|</Line>|' nameoffile.txt
The tool for editing text files in Unix, is called ed (as opposed to sed, which as the name implies is a stream editor).
ed was once intended as an interactive editor, but it can also easily scripted. The way ed works, is that all commands take an address parameter. The way to address a specific line is just the line number, and the way to change the addressed line(s) is the s command, which takes the same regexp that sed would. So, to change the 42nd line, you would write something like 42s/old/new/.
Here's the entire command:
FILENAME=/path/to/whereever
LINENUMBER=25462599
ed -- "${FILENAME}" <<-HERE
${LINENUMBER}s!</Topic>!</Line>!
w
q
HERE
The advantage of this is that ed is standardized, while the -i flag to sed is a proprietary GNU extension that is not available on a lot of systems.
Use "head" to get the first 25462598 lines and use "tail" to get the remaining lines (starting at 25462601). Though... for a 2GB file this will likely take a while.
Also are you sure the problem is just with that line and not somewhere previous (ie. the error looks like an XML parse error which might mean the actual problem is someplace else).
My shell script:
#!/bin/bash
awk -v line=$1 -v new_content="$2" '{
if (NR == line) {
print new_content;
} else {
print $0;
}
}' $3
Arguments:
first: line number you want change
second: text you want instead original line contents
third: file name
This script prints output to stdout then you need to redirect. Example:
./script.sh 5 "New fifth line text!" file.txt
You can improve it, for example, by taking care that all your arguments has expected values.

Resources