Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I have a requirement to write a shell script in csh to search and delete lines matching a pattern along with comments in previous line if any. For example if my file has the following lines

Shell script
#this is  a test
pattern1
format1
pattern2
format2
#format3
pattern3

If the search pattern is "pattern" The output should be as follows

Shell script
format1
format2

To be more precise the lines which have the pattern and the previous line if it begins with "#" should be deleted

Thanks for the help

share|improve this question
    
Try it now. I think I fixed the problem. –  Dennis Williamson Dec 2 '09 at 20:34
    
It's a shame that "grep -v" can't be used to do this, but adding the "-B 1" argument does about the opposite of what you want. Sigh. Maybe an "invert" flag for -B / -A should be added to GNU grep some day? –  Roboprog Dec 3 '09 at 3:25
add comment

5 Answers

Probably a better way to write this logically, but I think this might do it:

#!/usr/bin/perl
use strict;
use warnings;


my $previous_line = '';
while(<>) {
    if ( /pattern/ ) {
        if ( (! ($previous_line =~ /^#/)) && (! ($previous_line =~ /pattern/))) {
            print $previous_line;
        }
    } elsif (! ($previous_line =~ /pattern/)) {
        print $previous_line;
    }
    $previous_line = $_;
}
print $previous_line if not ($previous_line =~ /pattern/);

Basically, the loop is a line behind with the previous line. It says it is okay to print the previous line if:

  1. If The current line matches the pattern: Okay to print previous as long as the previous didn't also match pattern, or it was a comment.
  2. If this line is not pattern, it is okay to print the previous line as long as it didn't match pattern.

You can just save the code in a file and use it like: perl thefile.pl textfile_you_want_to_filter

share|improve this answer
    
Exactly how I'd do it more or less in anything (shell, perl, etc.), the logic is what matters. –  troyengel Dec 16 '09 at 1:12
add comment

First of all nobody should ever use csh for anything - it's outdated and un- (not "under") powered. Secondly, I doubt it's up to the task. Third, it's much more likely that awk, sed or even Perl will be a much better tool for the task.

awk '/^#/ {printf line; line=$0"\n"; next} /pattern/ {line=""} ! /pattern/ {printf line; print; line=""}'

Edit: fixed script to handle comment lines correctly

share|improve this answer
    
Thanks for the response. I tried to use the command but the result is not what i expected. The pattern and the comment are still there. The result I got is as follows Shell script #this is a testpattern1 format1 pattern2 format2 #format3pattern3 I want the line with the pattern and the comment in the previous line to be deleted –  Ashu Dec 2 '09 at 19:41
    
I'm not sure if the example is visible as expected. "#this is a test" is in a line and each word after that is in a spaerate line –  Ashu Dec 2 '09 at 19:43
    
Sorry for the confusion. I had made a mistake. The command works. Thanks –  Ashu Dec 2 '09 at 19:48
    
Hi, This command has some issues. If I change the file as follows #header #test Shell script #this is a test pattern1 format1 pattern2 format2 #format3 pattern3 The result is as follows #testShell script format1 format2 #header is deleted, #test and Shell script are merged. Please advise –  Ashu Dec 2 '09 at 19:58
    
It works . Thank you for the quick response –  Ashu Dec 2 '09 at 20:44
show 11 more comments

Here is a one-liner solution in Perl (not in C shell). You can modify the /pattern/ regular expression in the middle.

perl -ne 'if(/^#/){$c=$_}elsif(!/pattern/){print$c,$_;$c=""}else{$c=""}' <file.in
share|improve this answer
add comment

Here's a sed version. Some versions of sed may need parts of this separated into multiple -e clauses.

sed '$b;N;/^#.*\npattern.*$/ ! {P;D}; :c; $d; s/.*\n//;N;/^#.*\npattern.*$/ {bc}; /^pattern/d; D' patterns

Here's a script file version of that one-liner with comments:

#!/bin/sed -f

# Search for a combination of a comment followed by a pattern
# until that, print what you find.
$b
N
/^#.*\npattern.*$/ ! {
P
D
}

:c
# Got a comment and a pattern combination in pattern space.
# At the end of the file we simply exit
$d

# Else, we keep reading lines with `N' until we
# find a different one
s/.*\n//
N
/^#.*\npattern.*$/ {
bc
}

# Remove standalone lines that have "pattern"
/^pattern/d

# Remove the last instance of the combination
# and go back to the top
D

This is based on the script in info sed section 4.16 "Remove All Duplicated Lines" (uniq -u).

share|improve this answer
    
This command is not working –  Ashu Dec 3 '09 at 21:15
    
Using a variable or a literal? –  Dennis Williamson Dec 3 '09 at 21:48
    
If it's a variable, wrap it in single quotes: sed '$b;N;/^#.*\n'$pattern'.*$/ ! {P;D}; ... –  Dennis Williamson Dec 3 '09 at 21:50
add comment

Does it have to be shell scripted?

  1. open file with vi
  2. :g/<pattern>/d
  3. repeat as necessary for additional pattern-types unless you can regex the pattern
  4. :g/^#/d

can be effectively reproduced using sed if it has to be scripted

edit:

1.create file .sedscript:

/pattern/d
/^#/d

2.sed -f .sedscript <inputfile> > <outputfile>

This does not satisfy a requirement to delete the previous line, but your example doesn't seem to require that functionality.

share|improve this answer
    
It has to be scripted for reusability. –  Ashu Dec 3 '09 at 16:14
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.