2

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

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • Try it now. I think I fixed the problem. – Dennis Williamson Dec 02 '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 03 '09 at 03:25

5 Answers5

0

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

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • 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 –  Dec 02 '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 –  Dec 02 '09 at 19:43
  • Sorry for the confusion. I had made a mistake. The command works. Thanks –  Dec 02 '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 –  Dec 02 '09 at 19:58
  • I am tyying to incorporate the awk command into a shell script and its not working as expected. The variable for pattern is not being read. Any suggestions on this –  Dec 03 '09 at 16:00
  • The command i'm using in the cript is as follows cat $filename | awk '/^#/ {printf line; line=$0"\n"; next} /$pattern/ {line=""} ! /$pattern/ {printf line; print; line=""}' –  Dec 03 '09 at 16:04
  • Use awk variable passing: `awk -v pattern=$pattern '.../pattern/...'` – Dennis Williamson Dec 03 '09 at 17:07
  • By the way, don't forget to upvote and accept answer(s) to your questions if you find them useful. – Dennis Williamson Dec 03 '09 at 17:10
  • I am using the following script where I pass the pattern and the filename as parameters #!/usr/bin/csh set pattern=$argv[1] set filename=$argv[2] nawk -v pattern=$pattern '/^#/ {printf line; line=$0"\n"; next} /pattern/ {line=""} ! /pattern/ {printf line; print; line=""}' $filename awk '/^#/ {printf line; line=$0"\n"; next} /pattern/ {line=""} ! /pattern/ {printf line; print; line=""}' pettern=$pattern $filename Both the awk commands are not working. However if I substitute the variable with the actual pattern it works. –  Dec 03 '09 at 19:44
  • Sorry, I wasn't paying attention when I posted that comment about the variables. In order to use a variable for a match you have to use the match operator `~` instead of `//`. Something similar to `nawk -v pattern=$pattern '/^#/ {printf line; line=$0"\n"; next} $0 ~ pattern {line=""}` ... – Dennis Williamson Dec 03 '09 at 20:05
  • I tied the following command and it is deleting more lines then expected nawk -v pattern=$pattern '/^#/ {printf line; line=$0"\n"; next} $0 ~ pattern {line=""} ! $0 ~ pattern {printf line; print; li ne=""}' $filename –  Dec 03 '09 at 20:18
  • Did you paste that here? There's a space in the middle of the last reference to the variable "line". Also, it should be `$0 !~ pattern` instead of putting the "!" first. – Dennis Williamson Dec 03 '09 at 21:32
  • I have made the change and it works. Thanks for the help. What should be the command if I want to delete all the comments in the lines before the pattern For example if I have the following lines in my ile #format test #format1 #format2 #format3 pattern I should get the output as #format test –  Dec 03 '09 at 21:48
  • how do I alter the following command to display lines which match a particaular pattern and the cmments before the line matchung the pattern nawk -v pattern=$pattern '/^#/ {line=line$0"\n"; next} $0 ~ pattern {line=""} $0 ~ pattern {printf line; print; line=""}' $filename exomple of the file #comment1 #comment2 pattern1 pattern2 pattern1 Result should be #comment1 #comment2 pattern1 pattern1 Thanks for the help –  Dec 07 '09 at 20:56
0

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

Kyle Brandt
  • 82,107
  • 71
  • 302
  • 444
  • Exactly how I'd do it more or less in anything (shell, perl, etc.), the logic is what matters. –  Dec 16 '09 at 01:12
0

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
pts
  • 425
  • 1
  • 5
  • 15
0

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.

Greeblesnort
  • 1,739
  • 8
  • 10
0

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).

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148