LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Insert line on match only once with sed? (http://www.linuxquestions.org/questions/programming-9/insert-line-on-match-only-once-with-sed-657764/)

lhouk 07-23-2008 03:05 PM

Insert line on match only once with sed?
 
I thought this would be fairly straightforward, but it's got me stumped. The following sed command will find every blank line and insert the new line "aaaa" after each one:
Code:

sed '/^$/i\
aaaa'

But can I modify it to insert the new line only once, after just the first blank line, no matter how many blank lines are in my file? Thanks in advance to all who respond.

Mr. C. 07-23-2008 06:05 PM

Sed can be a pain to work with.

Does this work for you:

Code:

perl -e '$/=undef; $_=<>; s/\n\n/\naaaa\n/; print $_'  datafile

ghostdog74 07-23-2008 08:17 PM

Code:

awk 'NF==0{print;print "insert line";next}1' file

pixellany 07-23-2008 10:58 PM

I don't see a simple way in SED. (Perhaps Mr. C and ghostdog74 did not either.....;))

SED provides a mechanism for specifying which occurrence of a pattern to act on, but it works line by line.

Note that "i" will insert your new text BEFORE the line matching the pattern. To insert after, you want "a" (append).

Best SED (and AWK) tutorial here:
http://www.grymoire.com/Unix/Sed.html#

syg00 07-23-2008 11:17 PM

Just goes to prove - use the (best) tool for the job. Hammers don't make good screw-drivers.

pixellany 07-24-2008 12:05 AM

Quote:

Originally Posted by syg00 (Post 3224445)
Hammers don't make good screw-drivers.

Well, now.....that depends on the screw, doesn't it.......;). In my motorcycle repair days, we had a saying: "Get a bigger hammer". With what we were dealing with, that was the right answer surprisingly often.

Bash scripting is like having the world biggest collection of hammers---all sizes. We all have our favorite that we grab first.

Kenhelm 07-24-2008 07:31 AM

This inserts the newline "aaaa" only after the first blank line.
Code:

sed '1,/^$/ {/^$/a\
aaaa
}'


pixellany 07-24-2008 08:09 AM

Neat....

I didn't know that the range syntax could be nested like that

lhouk 07-24-2008 09:33 AM

Quote:

Originally Posted by Kenhelm (Post 3224868)
This inserts the newline "aaaa" only after the first blank line.
Code:

sed '1,/^$/ {/^$/a\
aaaa
}'


That's exactly what I was looking for! Thank you!

And pixellany, you are quite right:

Quote:

Originally Posted by pixellany (Post 3224433)
...Note that "i" will insert your new text BEFORE the line matching the pattern. To insert after, you want "a" (append)...

I wrote "after" in my original post, when I meant to say "before". :) But Kenhelm's code works just as well with "i" as with "a".

Also, thanks to Mr. C and ghostdog74 for their responses. I appreciate the help.

Mr. C. 07-24-2008 12:07 PM

The range isn't exactly nested; the command is

[2addr] function-list

in this case the [2addr] is the range line 1 to the first blank line. The function list looks like:

{
func1
func2
...
}

and this list contains only one function, which itself looks like:

[1addr]a

where [1addr] specifies any lines matching /^$/.

I used to love working out all sorts of problems with sed, but now no longer have the patience for it.
Nice job Kenhelm.

ps. I'm still awaiting someone to solve the sed palindrome checker I've challenge sed masters with several times. :-)

pixellany 07-24-2008 12:17 PM

Yes, I was sloppy when said range. What is "nested" here is the <<address--action>> functionality of SED. Range is a special case of address.

Nesting:

<<address---
<<address--action>>
>>

Can this nesting go deeper? For example:
<<range---
<<subrange---
<<address---action>>
>>
>>

Where is the Palindrome problem posted? (Somewhere I proposed a thread on scripting puzzles--should we revive that idea?)

Mr. C. 07-24-2008 01:11 PM

I've not posted the palindrome problem officially, but have mentioned it several times. Basically, the challenge is to make a sed script that recognizes any palindrome and outputs Yes, if it is, and No if it is not.

The range is in the definition of a 2addr (which means 0, 1, or 2 addresses), and addresses take a couple of forms, such as line number, or a pattern. Example 2addr's are 4, or 3,4, or 5,/somepat/, or /pat2/,$, etc.

Can sed "nest"; sure. As long as you live within its grammer, you can do what you're asking about:

Code:

$ cat in
Section AAA
        var1 = abba
        var2 = baab
        var3 = abab
EndSection

Section BBB
        var1 = nisse
        var2 = lasse
        var3 = salle

EndSection

$ cat sed.script2
1,$ {
=
/Section/ {
p
/End/ {
a\
This is the end
}
}
}

$ sed -nf sed.script2 in
1
Section AAA
2
3
4
5
EndSection
This is the end
6
7
Section BBB
8
9
10
11
12
EndSection
This is the end


pixellany 07-24-2008 02:42 PM

Just ran some more tests....

Not only can you nest address--action pairs, but the subaddresses can overlap.

eg:
sed '1,6{1,3<do something>;2,5<do something else>;4,6<do more stuff>}' file

If each action is a change, then they happen in order---ie lines 1 thru 3 get changed, and then the changes for 2 thru 5 get applied, etc.

But, if--eg-- the action for 2,5 is delete, then the next statement acts on what WAS lines 4,6.

WOW!!! The opportunities to write obfuscated SED code just went way up.......;)

Mr. C. 07-24-2008 02:50 PM

Exactly. Just think of 2addr or 1addr as asking "does the current line number or pattern (space) match the address(es) specified in 2addr or 1addr? ... If so, do stuff".

Kenhelm 07-25-2008 03:07 AM

An answer to Mr. C.'s sed palindrome checker problem:-
Code:

#!/bin/bash
# If $1 is a palindrome output 'Yes'; otherwise output 'No'
echo $1 | sed 's/.*/&\n&\nNo\nYes/' |  # Make 4 lines: $1/$1/No/Yes
sed '2 {/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.// }' | # 'rev' line 2
sed '$!N; /^\(.*\)\n\1$/!P; D' |                    # Emulate 'uniq'
sed -n '3p'                                        # Output line 3

The method:-
Set up 4 lines,
$1
$1
No
Yes

Emulate 'rev' only on line 2 (e.g. "abcde" becomes "edcba")

Emulate 'uniq': if $1 is a palindrome line 2 is deleted so "Yes" moves up from line 4 to replace "No" on line 3.

Output line 3.

The emulations of 'rev' and 'uniq' are from
'Useful One-line Scripts For sed' Compiled by Eric Pement
http://sed.sourceforge.net/sed1line.txt


All times are GMT -5. The time now is 08:23 AM.