2

NOTE: I'm aware of this question: How to grep-inverse-match and exclude "before" and "after" lines. Mine is not a duplicate as had been previously cited, the answers to that question also deletes the pattern, while in this question the pattern itself must be preserved.


I am trying to delete n lines after a pattern and m lines before a pattern (without deleting pattern). For example, if the file is:

1
2
3
4
5
6
7
8
9

If, Pattern = 5, and n = 2 and m = 3. Then:

1
5
8
9

Could you please suggest how to do this?


Bonus: It would be great if we could set either m or n = 0 in the same code. For example. If we set m=0 and n=1, in the above example, we should get:

1
2
3
4
5
7
8
9
1
  • It is not duplicate as the cited link also deletes the pattern, while in this question pattern itself must be preserved.
    – Porcupine
    Oct 7, 2018 at 15:29

2 Answers 2

3

To answer your general situation, we construct an appropriate ed code ahead of time based on the kind of inputs given.

 re=5 n=2 m=3
 code=$(
   prev="/$re/-$m,/$re/-1d"
   next="/$re/+1,/$re/+${n}d"
    case "$m/$n" in
       0/0) set -- ;;
       0/?*) set -- "$next" "w" ;;
       ?*/0) set -- "$prev" "w" ;;
         *) set -- "$prev" "$next" "w" ;;
    esac
    printf '%s\n' ${1+"$@"} "q" 
 ) 
 ed -s filename - <<eof
 $code
 eof

One way could be : this uses the ed editor to perform relative addressing as that is what your problem is centered around.

 n=3 m=2 re=5
 ed -s filename - <<eof
 /$re/-$m,/$re/-1d
 .+1,.+${n}d
 wq
 eof

Explanation:

 1. Line 3 after var substitution becomes
            /5/-2,/5/-1
      What it does is, sitting on line which satisfies the regex /5/, it looks 2 lines behind and stops looking 1 line before the /5/ line or the current line and deletes that bunch. Remember the current line is not deleted.

   2.  Line 4, after var sub becomes
                .+1,.+3d
       . is the nickname for the current line, which in our case is /5/
       So, starting fron one line after the current upto 3 lines after the current, delete them all. Note the current line is still untouched.

  3. Line 5 is wq which means save the modified file back onto itself and quit.

For more on info google the manual for gnu ed editor. 
4
  • Hi! Thanks it works. Could you please explain line 3, 4 and 5? I understand that /$re/ is pattern. What regex does this support?
    – Porcupine
    Oct 7, 2018 at 9:15
  • Thanks for the explanation, can we make this code more versatile, such that it also works when either m or n are 0?
    – Porcupine
    Oct 7, 2018 at 12:09
  • So in that scenario what's the output you expect. Fill that in the main body of this question. Oct 7, 2018 at 12:52
  • I have updated the question, please look at the bottom.
    – Porcupine
    Oct 7, 2018 at 15:30
2

Let's say you have a file - numbers.list:

1
2
3
4
5
6
7
8
9

Here's a script that should accomplish what you're after:

#!/bin/bash
if [ -z "$1" ]; then
    echo >&2 "error: no file specified"
    exit 1
fi
if [ ! -f "$1" ]; then
    echo >&2 "error: $1 is not a file"
    exit 1
fi
if [ -z "$2" ]; then
    echo >&2 "error: no pattern specified"
    exit 1
fi
grep "$2" "$1" >/dev/null 2>&1
if [ ! 0 -eq "$?" ]; then
    echo >&2 "error: pattern $2 not found in $1"
    exit 1
fi
MATCH_FILE="$1"
MATCH_PATTERN="$2"
MATCH_DELETE_AFTER=0
MATCH_DELETE_BEFORE=0
if [ ! -z "$3" ]; then
    MATCH_DELETE_AFTER="$3"
fi
if [ ! -z "$4" ]; then
    MATCH_DELETE_BEFORE="$4"
fi
MATCH_FILE_LINE="$( grep -n "$2" "$1" | cut -d: -f1 )"
#
# print matching `head` minus MATCH_DELETE_BEFORE lines
cat "$1" \
    | head -n "$( expr "$MATCH_FILE_LINE" - "$MATCH_DELETE_BEFORE" - 1 )"
#
# print matching line
cat "$1" \
    | head -n "$MATCH_FILE_LINE" \
    | tail -n 1
#
# print matching `tail` minus MATCH_DELETE_AFTER lines
cat "$1" \
    | tail -n "$( expr "$( wc -l "$1" | cut -d' ' -f1 )" - "$MATCH_FILE_LINE" - "$MATCH_DELETE_AFTER" )"

Example usage: ./matching.sh numbers.list 5 2 3

Note: This solution will not work as intended if expr evaluates negative values - up to you if you want to implement checks to avoid this behavior.

1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .