It's a lot easier to delete specific lines using sed. Also you can have sed do replacements to the n'th instance of something. Doing that in Perl is a bit more complicated and a lot less succint.
Which line? Why p? I can make assumptions that this means “line 10, print” but easy-to-explain could go a small step further and complete the explanation.
One of these days I need to get around to learning awk. In the meantime, I've learned some of the deeper, stateful, features of sed. For instance, you mentioned wanting to only output a line if it was preceded by another. Here's a sed command that does so:
The `-n` tells sed no to print anything out. By default, sed prints out whatever is left when processing. We'll tell it with the `p` command when to do so.
sed -ne 'x'
`-e` indicates we are specifying one of the scripts sed will execute. The command `x` switches the current line with whatever is in the hold buffer. We'll do this on every line.
sed -ne 'x' -e '/PREV/
The next command will only run on lines that contain `PREV`. But, because we've been putting lines in the hold buffer, we'll only execute on lines after `PREV` when it has been switched out of the hold buffer.
sed -ne 'x' -e '/PREV/ { ... }'
The braces indicate all commands should be run when we see this match.
sed -ne 'x' -e '/PREV/ { x; ... }'
First, we switch the hold buffer with the line buffer.
sed -ne 'x' -e '/PREV/ { x; /CURR/ p; ... }'
Then, we only print out the line if it contains CURR.
sed -ne 'x' -e '/PREV/ {x; /CURR/ p; x}'
Finally, we switch them back in case there is overlap in our matches. (Give `echo -e "PREV\nPREVCURR\nCURR\nCURR\nPREV\nRED"` a try with this.)
All that said, I'm pretty sure the `awk` script is much simpler and more direct, but I wanted to share how one might accomplish this was sed.
The time I spent learning this probably would've been better spend on awk, but this tutorial[0], was so good and so easy, it taught me nearly everything I know about sed.
Hah, lol, thanks, Today I Learnt ;) Will try using this next time, probably.
That said, there's one extra advantage with the awk script, that by rearranging the expressions appropriately, I can choose whether to include or exclude the the first and last line in the output (i.e. 'FIRST; p; LAST' vs. 'FIRST; LAST; p' or 'p; FIRST; LAST', etc.) Is this also possible with sed? :)
You can get the same behaviour with sed without "programmability":
$ sed -n '/^func Test/,/^}/p' file
Which means: match 'func Test' at the beginning of a line and print (p command at the end) until you find a line beginnning with '}'.
This is because the print command (p) accepts 2 addresses to delimit a range. I used regexes as addresses but, for instance, can use line numbers also:
$ sed -n '10,20p' file
This prints file's line from 10 to 20.
By default, sed prints the pattern space (modifications to each line) at the end of the script. The -n I used is to avoid that.
Even though, the ruby one-liner seems more legible, it is a lot longer (and probably a lot slower).
In this case I don't see what's wrong with the sed, awk or grep commands (fast and short).
To explain how they work
sed -i '/^$/d' input.txt # for every line matching '^$' (empty line) apply 'd' (delete the line).
grep -v '^$' # print every line not matching '^$' (empty line)
awk 'NF' input.txt
# 'NF' stand for Number of Fields (this is equivalent to number of columns
# in a table). If the number of fields is 0 the line must be empty. In awk
# 0 is false and non-zero is true. Thus when 'NF' is non-zero (non-empty)
# it is true, and the line will be printed.
The "print lines by number" was there because earlier versions of ack had a `--line=N` feature, where you could say "ack --line=15-18" and print those four lines. I dropped it because it was hardly any better than using sed.
If you've got suggestions on improvements, please submit an issue. I'd love to hear them.
Which deletes the label itself, so you don't need the subsequent `grep -v ':$'`, but then you also aren't allowed to put any statements on the same line as the label.
> echo "$FOO" | sed -e ' s/^[[:space:]]//g; s/[[:space:]]$//g '
Your example fails when $FOO is "-n", for instance. Also the g modifiers are redundant in your example since there's only one beginning of line per line and one end of line per line.
I would instead write this:
sed -r 's/^\s+|\s+$//g' <<< "$FOO"
EDIT: I think I see now what you probably thought would happen by using g, but no it wouldn't remove multiple spaces. So, you example also fails when $FOO is " x" (using 2 or more spaces at the ends).
sed: sed -n '4!p'
awk: awk 'NR != 4'
perl: perl -ne '$. != 4 && print'
Not much between them really.
reply