Description
  • A line is said to be `matched' if it contains a substring matched a given regex.
  • In the following example, we want to get the 3rd, the 5th, 8th, and the 9th lines who are matched `PATH'. The case distinctions are ignored in this example.
Raw Input Desired Output
(1)pathmunge () {
    if ! echo $(2)PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
       if [ "$2" = "after" ] ; then
          (3)PATH=$PATH:$1
       else
          (4)PATH=$1:$PATH
       fi
    fi
}
if [ "$EUID" = "0" ]; then
    (5)pathmunge /sbin
    (6)pathmunge /usr/sbin
    (7)pathmunge /usr/local/sbin
else
    (8)pathmunge /usr/local/sbin after
    (9)pathmunge /usr/sbin after
    (10)pathmunge /sbin after
fi
HOSTNAME=`/bin/hostname 2>/dev/null`
HISTSIZE=1000
export (11)PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
unset (12)pathmunge
          (3)PATH=$PATH:$1
    (5)pathmunge /sbin
    (8)pathmunge /usr/local/sbin after
    (9)pathmunge /usr/sbin after
Script and Comments
Script1
[ 1] /PATH/I!d
[ 2] G
[ 3] s/$/#/
[ 4] /\n(#{3}|#{5}|#{8}|#{9})$/P
[ 5] s/^[^\n]*\n//
[ 6] h
[ 7] d
Comments -r
    • A counter is necessary to keep how many matched lines we have examined so far.
    • The value of the counter is keep in HS as the equivalent number of hash marks.
    • Non-matched lines are deleted by Step [1]. Therefore, they will not be examined by the following steps.
    • The I-modifier instructs GNU sed to ignore case distinctions.
  1. To determine whether a matched line has to be printed or not, we have to get its ordinal by increasing the counter by one:
    • Step [2] appends the value of the counter (contents of HS) to PS, and separates the original data of the line and the value with a newline character.
    • If the line is the 3rd, 5th, 8th, or 9th matched line, Step [4] prints it.
    • Then the updated value of counter has to be written back to HS by Steps [5] and [6].
  2. Step [7] deletes the current line and then starts a new cycle to examine the next line.
Script2
[ 1] /PATH/I!d
[ 2] G
[ 3] s/$/#/
[ 4] /\n#{9}$/{
[ 5] s/\n.*//
[ 6] q
[ 7] }
[ 8] /\n(#{3}|#{5}|#{8})$/P
[ 9] s/^[^\n]*\n//
[10] h
[11] d
Comments -r
  1. The first script will keep examining lines after the last matched line in which we are interested. This script will instruct sed to stop instead.
  2. After reading the last matched line we are interested, the 9th matched line in this example:
    • The regex of Step [4] matches,
    • Step [5] removes the counter value from PS.
    • Step [6] makes sed print the contents of PS, and then stop.