mrng(){ sed "$(set -f;unset IFS rng l;n='
';[ -n "$ZSH_VERSION" ] && emulate sh
prng() for m do rng=${r%%"$m"*}${r##*"$m"} _l=$((_l+1))
printf "\n\n%s\n/$pat/{\n\t:$l.$_l\n\tn" $m
printf "\n\n%s\n\t/$pat/b$l.$_l.0" $rng
printf "\n\tb$l.$_l\n\t:$l.$_l.0\n}"
done
pat=$( printf %s "${1:-%m}$n"| sed -n 's/%/&&/g;l'|
sed ":n$n\$!N;s/\\\\\n//;tn${n}s/\$$//"); shift
r=$( locale -c LC_TIME|sed '4!d;y/;/ /')
for m do case $m in (-) rng=$r ;;
(-*) rng=${r%%"${m#-}"*}${m#-} ;;
(*-) rng=${m%-}${r##*"${m%-}"} ;;
(*-*) rng=${m%%-*}${r##*"${m%%-*}"} ;
case $rng in (*${m##*-}*)
rng=${rng%%"${m##*-}"*}${m##*-} ;;(*)
rng=$rng\ ${r%%"${m##*-}"*}${m##*-};;esac
;;esac; : $((l+=1))
prng ${rng:="$m"}; unset rng
done| sed " 1d;s/.*\(...\)\(\n\)\(.*[^%]\(%%\)*\)%m/\2\1\2\3\1/
/./!{N;N$n};/\n/D"
);d"
}
This is a shell function - you'll either need to adapt it into a shell-script if that's how you plan to call it, or to evaluate it in the current shell. You can call it like:
mrng "$pat" Jan-Mar Jun Sep-Nov <infile
It accepts open-ended ranges as well, such as -
to mean all or Mar-
to mean from March through December. And arguments don't have to be ranges - as above, Jun
is fine.
In fact, though, it doesn't really interpret the month names at all - it collects those from the locale
utility (which is a dependency) and works with whatever the current locale says the 3-char month names are.
It can do wrap-around ranges, and, in truth, almost all of them actually do wrap-around, or, maybe it is better to say, accumulate, anyway.
It expects its first argument to be a sed
compatible BRE pattern - with the exception that wherever you would expect to encounter the month name, you should use %m
instead. You can insert several %m
s as well - which you might do if you only wanted to match lines like Mar...Mar
- Jun...Jun
. Probably that is not incredibly useful, but maybe...?
Though it looks complicated, more than half of that is devoted to arg-parsing - the sed
is relatively simple, after all. For example, if I do:
mrng %m Dec-Jan
...it will generate a sed
script that looks like:
/Dec/{
:1.1
n
/Jan/b1.1.0
/Feb/b1.1.0
/Mar/b1.1.0
/Apr/b1.1.0
/May/b1.1.0
/Jun/b1.1.0
/Jul/b1.1.0
/Aug/b1.1.0
/Sep/b1.1.0
/Oct/b1.1.0
/Nov/b1.1.0
b1.1
:1.1.0
}
/Jan/{
:1.2
n
/Feb/b1.2.0
/Mar/b1.2.0
/Apr/b1.2.0
/May/b1.2.0
/Jun/b1.2.0
/Jul/b1.2.0
/Aug/b1.2.0
/Sep/b1.2.0
/Oct/b1.2.0
/Nov/b1.2.0
/Dec/b1.2.0
b1.2
:1.2.0
};d
...which winds up being a lot of code, but most of which is not evaluated most of the time. On a typical line, it will check if it matches Dec, and, if not, if it matches Jan, and, if not, delete it from output.
But once it does match one of those patterns, it will begin a simple branch loop. So, given the above example, if a line matches Dec then it will be printed and overwritten with the n
ext input line. If the new line matches any month but Dec, sed
b
ranches to the :1.1.0
label - which means that line has yet to be evaluated against Jan
- where it will get similar treatment - but will not be evaluated against any month before Dec. If it does not match any month but Dec, then sed
b
ranches up to the :1.1
label, which gets the line printed and the n
ext pulled in and etc.
If I did -
instead, then it would gen a function similar to the above - each bearing its own unique :
label - for every month within the range. This means that the command-line arguments have cumulative effect. Some examples:
printf %s\\n 'not a month' May 'not a month' 'also not a month' Apr |
m_rng %m Apr May
The above prints:
May
not a month
also not a month
Because May
comes before Apr
in input, but after Apr
on the command-line. However, it is a pretty rough heuristic. The input is processed in the order of the command-line args, but as soon as a full-cycle is made the processing starts over again, so...
printf %s\\n 'not a month' May 'not a month' 'also not a month' Jun Apr |
m_rng %m Apr May
...prints...
May
not a month
also not a month
Apr
Because the cycle breaks at Jun
, the line is deleted, and processing starts again from the top w/ the next input line - Apr
.
Anyway, for your pattern you should use:
mrng '^\([^ ]\{1,\} *\)\{3\}%m' [month args]
awk '/Feb|Apr|May/' file
(assuming there is a date for every record--your question isn't clear about that)...Feb
-Apr
lines, simply runsed -n '/Feb/,/May/{/May/!p}' infile
Since your file is sorted by date, this will print from the first line matchingFeb
up to but not including the first line matchingMay
(which means it prints up to the last line matchingApr
). Similarly, forFeb
-Nov
you'd dosed -n '/Feb/,/Dec/{/Dec/!p}' infile
. Let me know if I misunderstood your question...