grep (/sed/awk) 月份范围

grep (/sed/awk) 月份范围

我已经四处寻找解决方案,但是搜索这样一个特定问题的解决方案有点困难(要在谷歌中输入很多单词)。

因此,我有一个包含如下数据的文件(省略了不相关的数据):

... ... 2014 年 1 月 1 日 ... ... ...
... ... 2014 年 1 月 02 日 ... ... ...
(...)
... ... 2014 年 3 月 1 日 ... ... ...
(...)

我们可以假设数据按日期排序。因此,我想做的一件事就是捕捉适合一系列月份的线条。例如,如果我的范围是二月到四月,我想获取月份列中带有FebMar、 和 的所有行。Apr

为了取消指定问题并稍微抽象一下我的请求,我真的只想获取与某个模式匹配的第一行,并捕获从那时起的所有行,直到我到达另一个模式的最后一次出现(以及所有与特定模式匹配的行被分组在一起)。

我找到了一些类似问题的解决方案,它们使用grepsedawk。我不确定在这种情况下哪个最好。

这一切都是在 bash 脚本中完成的,所以我考虑过拥有一个包含所有月份“代码”的数组 [ Jan, Feb, Mar, ...] ,并以某种方式检查我是否在我想要的范围内,但我觉得这个问题可能有一个更优雅的解决方案。

编辑:@jasonwryan 好吧,我实际上为脚本提供了一个范围,因此 './script.sh --month "Jan, Apr"' 按月对数据文件进行排序,然后应该能够 grep 任意范围(从在这种情况下是一月到四月)。因此, awk '/Feb|Apr|May/' 适用于这种情况,但如果我想要 2 月至 11 月,那就不同了 (awk '/Feb|Mar|Apr|May|Jun|Jul|Aug|Sept| 11 月/' 文件)。因此,很难基于任意范围实现自动化。另外,每个月至少应该有一次排队(也可能每天都有)。忘记具体说明了。

答案1

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"       
}

这是一个 shell 函数 - 如果您打算这样调用它,则需要将其改编为 shell 脚本,或者在当前 shell 中对其进行计算。你可以这样称呼它:

mrng "$pat" Jan-Mar Jun Sep-Nov <infile

它也接受开放式范围,例如-表示全部Mar-指从三月到十二月。而论点则不然成为范围 - 如上所述,Jun就可以了。

但事实上,它根本没有真正解释月份名称 - 它从实用locale程序收集这些名称(这是一个依赖项)并适用于当前区域设置所说的 3 字符月份名称。

它可以做环绕范围,事实上,几乎所有范围实际上环绕,或者,也许更好的说法是,无论如何,积累。

它期望它的第一个参数是sed兼容的 BRE 模式 - 例外的是,无论您在哪里期望遇到月份名称,都应该使用月份%m名称。您也可以插入多个%ms - 如果您只想匹配类似Mar...Mar- 的行,则可以这样做Jun...Jun。也许这并不是非常有用,但也许......?

虽然看起来很复杂,但其中一半以上用于 arg 解析 -sed毕竟相对简单。例如,如果我这样做:

mrng %m Dec-Jan

...它将生成一个sed如下所示的脚本:

/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

...最终会产生大量代码,但其中大部分在大多数情况下都不会被评估。在典型的行中,它将检查它是否与 Dec 匹配,如果不匹配,则检查是否与 Jan 匹配,如果不匹配,则将其从输出中删除。

但是一旦它确实匹配这些模式之一,它将开始一个简单的分支循环。因此,在上面的示例中,如果一行与 Dec 匹配,那么它将被打印并用next 输入行覆盖。如果新行匹配任何月份12 月,sed b牧场标签:1.1.0- 这意味着该生产线尚未进行评估Jan- 它将得到类似的处理 - 但不会针对任何月份进行评估如果它与除 Dec 之外的任何月份都不匹配,则sed b向上移动到:1.1标签,打印该行并n拉入分机等。

如果我-这样做,那么它将生成一个与上面类似的函数 - 每个函数都有自己独特的:标签 - 对于范围内的每个月。这意味着命令行参数具有累积效果。一些例子:

printf %s\\n 'not a month' May 'not a month' 'also not a month' Apr |
m_rng %m Apr May

以上打印:

May
not a month
also not a month

因为在输入中May位于前面Apr,但Apr在命令行中位于后面。然而,这是一个相当粗略的启发式。输入按照命令行参数的顺序进行处理,但是一旦完成完整周期,处理就会重新开始,所以......

printf %s\\n 'not a month' May 'not a month' 'also not a month' Jun Apr |
m_rng %m Apr May

...印刷...

May
not a month
also not a month
Apr

因为循环在 处中断Jun,所以该行被删除,并且处理再次从顶部开始,并带有下一个输入行 - Apr

无论如何,对于您的模式,您应该使用:

mrng '^\([^ ]\{1,\}  *\)\{3\}%m' [month args]

相关内容