ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
But for millgates' version, a new line would be printed at the top since the hold space is always left with a newline if something is appended to it even if it's empty so I suggest doing something like this:
sed -n '
/\/tmp/,/^$/ # for all lines between /tmp an an empty line
H; # append current line to hold buffer
/^$/ { # if the line is empty (end of each "/tmp" -- "empty line" block)
x; # swap contents of hold buffer and pattern buffer
# this will, unfortunately, put the pattern buffer into hold space
# and produce an extra blank line
/String1/p # if the text that we've just put from hold space to pattern space
# contains String1, print it
}
' <infile
Anyway, here is an awk solution. Not really complicated, not super-short either. Perhaps someone can find a better one:
But for millgates' version, a new line would be printed at the top since the hold space is always left with a newline if something is appended to it even if it's empty so I suggest doing something like this:
@danielbmartin If you would accept awk you should have said so as it's a lot easier But anyway thanks to your post I was able to challenge myself into hacking sed, and that's the first time I was able to make use of the hold space.
The changes to sed was about the newline inserted at the beginning, and not at the end, just in case.
@danielbmartin If you would accept awk you should have said so as it's a lot easier
Sure, I'll accept any working solution. I pounded the keyboard to build an awk solution, succeeded, and was about to post it when I read the post by millgates. His awk is far simpler than my if-then-else kludge. Did we all learn something? If so, let's break for lunch!
This problem has been very well solved by several contributors.
As a learning exercise I created a different solution composed of multiple invocations of sed. It worked, and then I attempted to combine all the seds into one, and was unsuccessful. The purpose of this post is to ask "what did I do wrong?"
This is the InFile ...
Code:
bad line 1
bad line 2
bad line 3
bad line 4
/tmp
good line 1
good line 2
good line 3
String1
good line 4
good line 5
good line 6
bad line 5
bad line 6
bad line 7
bad line 1
bad line 2
bad line 3
bad line 4
/tmp
good line 11
good line 12
good line 13
String2
good line 14
good line 15
good line 16
bad line 15
bad line 16
bad line 17
... this is the desired OutFile...
Code:
/tmp
good line 11
good line 12
good line 13
String2
good line 14
good line 15
good line 16
This code works ...
Code:
echo; echo "Method #1 of LQ Member danielbmartin (using only sed)"
rm $OutFile
sed ':a;N;$!ba;s/\n/~/g' $InFile \
|sed 's/~\/tmp/\n\/tmp/g' \
|sed '/String2/!d' \
|sed 's/\(.*\)\(~~.*\)/\1~/g' \
|sed 's/~/\n/g' \
>$OutFile
echo "OutFile ..."; cat $OutFile; echo "End Of File"
... and this derivative version works ...
Code:
echo; echo "Method #2 of LQ Member danielbmartin (using only sed)"
rm $OutFile
sed ':a;N;$!ba;s/\n/~/g; s/~\/tmp/\n\/tmp/g' $InFile \
|sed '/String2/!d; s/\(.*\)\(~~.*\)/\1~/g; s/~/\n/g' >$OutFile
echo "OutFile ..."; cat $OutFile; echo "End Of File"
... but this derivative version fails!
Code:
echo; echo "Method #3 of LQ Member danielbmartin (using only sed)"
rm $OutFile
sed ':a;N;$!ba;s/\n/~/g; s/~\/tmp/\n\/tmp/g; /String2/!d; s/\(.*\)\(~~.*\)/\1~/g; s/~/\n/g' $InFile >$OutFile
echo "OutFile ..."; cat $OutFile; echo "End Of File"
where "g" represents a "good line", "b" represents a "bad line", "t" is the "/tmp" and "s" is the string we're lookong for.
Now, your first example, if I understand correctly:
Code:
sed ':a;N;$!ba;s/\n/~/g' $InFile # this reads the entire file to pattern space
# and replaces newlines with ~
| # produces a single line
v
b~b~~t~g~s~g~g~~b~b~~t~b~b~
|
v
sed 's/~t/\nt/g' # this puts a newline before every t in the string
| # so now we have one line for every t block
v
b~b~ # these lines are read ONE BY ONE by the following
t~g~s~g~g~~b~b~ # sed !!!
t~b~b~
|
V
sed '/s/!d' # this deletes all lines that don't contain s !!!
|
v
t~g~s~g~g~~b~b~
|
v
sed 's/\(.*\)\(~~.*\)/\1~/g' # this cuts off everything from the last ~~ on.
| # please note that this will break if there are more than one
| # block not starting with t after the matching block, because
v # the first .* is greedy
t~g~s~g~g~
|
v
sed 's/~/\n/g' # this will replace ~ with \n so we get the block we wanted
| # in the first place:
v
t
g
s
g
g
g
Ok, now your last example:
Code:
:a;N;$!ba;s/\n/~/g; s/~t/\nt/g # same as in the first
| # example, results in following
v
b~b~
t~g~s~g~g~~b~b~
t~b~b~
|
v
/s/!d; # this deletes the pattern space if it does not contain s
| # but the entire file is now in the pattern space!!
V # and it does contain s! So nothing gets deleted
b~b~
t~g~s~g~g~~b~b~
t~b~b~
|
v
s/\(.*\)\(~~.*\)/\1~/g # this removes everything after
| # the LAST ~~
v
b~b~
t~g~s~g~g~
|
v
s/~/\n/g # replace ~ with \n again
|
v
b
b
t
g
s
g
g
Not tested, so I might have overlooked something somewhere.
/s/!d; # this deletes the pattern space if it does not contain s
| # but the entire file is now in the pattern space!!
V # and it does contain s! So nothing gets deleted
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.