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\ |
Sed can be a pain to work with.
Does this work for you: Code:
perl -e '$/=undef; $_=<>; s/\n\n/\naaaa\n/; print $_' datafile |
Code:
awk 'NF==0{print;print "insert line";next}1' file |
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# |
Just goes to prove - use the (best) tool for the job. Hammers don't make good screw-drivers.
|
Quote:
Bash scripting is like having the world biggest collection of hammers---all sizes. We all have our favorite that we grab first. |
This inserts the newline "aaaa" only after the first blank line.
Code:
sed '1,/^$/ {/^$/a\ |
Neat....
I didn't know that the range syntax could be nested like that |
Quote:
And pixellany, you are quite right: Quote:
Also, thanks to Mr. C and ghostdog74 for their responses. I appreciate the help. |
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. :-) |
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?) |
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 |
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.......;) |
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".
|
An answer to Mr. C.'s sed palindrome checker problem:-
Code:
#!/bin/bash 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 |
Hmmm, not working here.
Code:
$ echo "I am abba abba am I" | bash ./pal.sh Code:
$ echo "I am abba abba am I" | ./pal.sh |
Is there a prize for solving this puzzle?
How many people are known to have solved it? When you say "one sed script", I read it to mean that the word "sed" can only appear once. For example, this would be legal: sed -e 'stuff' -e 'morestuff' ....etc. |
Quote:
Ok, maybe I'll send $5 US. PM/email me the answers, and I'll post solutions if you want to allow everyone to get a chance to solve. Quote:
Quote:
Code:
$ sed -f sedfile |
Quote:
Code:
$ echo "No workie" | bash ./pal.sh Code:
$ bash ./pal.sh "No workie" To convert the script to use piped input just remove echo $1 | from the first line of the script code and then using GNU sed version 4.1.4 it works with all those examples of piped input where you gave it as not working. Quote:
|
I saw and knew that! Code that requires an argument should error out when there is no argument present. Since sed is a filter, a sed script should also act in like fashion. The challenge is "make a sed script". What you have is a shell script that calls sed ... several times. But still nice!
Since I didn't indicate which platform, it should work on any platform. Obviously this isn't reasonable if you don't have multiple environments in which to test. So we'll just call portability a bonus. |
Simple and efficacious.
Quote:
|
Very nice.
No need for your /g flag on the substitution in the loop. |
Really. Its necessary only in the first command of the sed. But because it dont affect the result, I forgot remove it.
_ |
Need help with the same topic...
Im trying to search a text file for a specific string, if the string is present, i'd like to insert a newline below the string with a value.
This works fine: Code:
#!/bin/sh This inserts TEST into the file below the search criteria. But when I need to supply the real world string, sed blows up. This example works via cmd line: Code:
|
To generally replace TEST with some arbitrary string in your shell script, you'll need to escape the metacharacters for both sed's consumption, and the shell as well. This get's tricky very quickly. Any time you can eliminate one quoting layer the better. So you might find perl's qr// operator easier to use.
How / where is the TEST string being assigned? |
The string im searching for is in a file that grows, but there's only one occurrence of the string in the file. Once I find the string, I need to write something along the lines of this right below the string.
proxy /* http://server-tstb.com.com:12345/* server-tstb.com.com |
Ok, this isn't so bad the via the shell. Since that's the direction you've taken, let's go that way:
Code:
#!/bin/bash |
Another approach to the palindrome challenge
All though I saw a solution in this discussion, it did niot solve the problem completely inside sed.
This solution does just that by keeping the original word in the hold space. This is just a filter: words go in, palindromes come out. It does not adres the Yes/No problem, but that is trivial. #!/usr/bin/ksh sed -n ' h s/^/\n/ :again s/\(.*\n\)\(.\)/\2\1/ t again G /^\(.*\)\n\n\1$/{ x p } ' "$@" |
Welcome to the party!
Good solution. To be pedantic, this is a KSH script, not a sed script. The language is KSH, which happens to call the sed command line utility with a sed script argument. :-) |
Yup, you are pedantic... but, aren't we all?
Just put everything between the single quotes in a file (palindromes.sed) and run sed -f palindromes.sed file... It will then be 100% pure sed :-) The shell in the example doesn't add anything at all (just turns the whole thing in a convenient filter form). |
Quote:
The form I called pedantic requires 2 processes. Filters should by nature, since you don't know how they will be called, be as cheap as possible, and no impose unnecessary performance penalties. There is too easy a solution around that penalty in this case. Filters should not require a user to specify arbitrary script files to include. Filters should not require a user to type out the filter script on the command line. So, the best solution, requires 1 process invocation, no specifying of a script file, or typing of a script. Use the kernels built in interpreter execution syntax instead, placing the contents below into a file that is then made executable: Code:
#!/usr/pkg/bin/gsed -nf |
OK, yes I did consider doing it that way (did forget about the -n though).
I agree completely. |
In case anyone else uses the posted sed script - I believe one correction is needed ---
instead of sed '1,/^$/ {/^$/a\ aaaa }' it should be sed '0,/^$/ {/^$/a\ aaaa }' The difference is apparent only for an input in which both the first two lines are blank. The originally posted script will add the aaaa line twice. The corrected one will do so only for the first blank line as specified. John |
sed might not be able to recognize palindromes
I am not totally certain about this, but I believe that sed cannot recognize arbitrarily long palindromes. Sed has the power of regular expressions, which is equivalent to deterministic finite automaton (DFA) which is also provably equivalent to a non-deterministic finite automaton (NDFA, also known as NFA). But this is not sufficient to recognize arbitrarily long palindromes. Recognizing arbitrarily long palindromes requires the power of a push-down automaton (PDA). The NFA is not equivalent to the capability of a PDA.
|
Just in case another awk alternative might be considered:
Code:
awk '/^$/ && !f++ && gsub(//,"&\nnext")||1' file |
This thread is over 2 years old!
|
oops ... sorry ... didn't look at date of original :( my bad :redface:
|
Variables
Quote:
|
Yes - simply use double-quotes instead of single and then explicitly quote the $ and \ chars :
e.g. addw=foobar;echo -e "\\n\\n" | sed "0,/^\$/ {/^\$/ a \\ ${addw} }" |
All times are GMT -5. The time now is 08:09 PM. |