我已经四处寻找解决方案,但是搜索这样一个特定问题的解决方案有点困难(要在谷歌中输入很多单词)。
因此,我有一个包含如下数据的文件(省略了不相关的数据):
... ... 2014 年 1 月 1 日 ... ... ... ... ... 2014 年 1 月 02 日 ... ... ... (...) ... ... 2014 年 3 月 1 日 ... ... ... (...)
我们可以假设数据按日期排序。因此,我想做的一件事就是捕捉适合一系列月份的线条。例如,如果我的范围是二月到四月,我想获取月份列中带有Feb
、Mar
、 和 的所有行。Apr
为了取消指定问题并稍微抽象一下我的请求,我真的只想获取与某个模式匹配的第一行,并捕获从那时起的所有行,直到我到达另一个模式的最后一次出现(以及所有与特定模式匹配的行被分组在一起)。
我找到了一些类似问题的解决方案,它们使用grep
、sed
或awk
。我不确定在这种情况下哪个最好。
这一切都是在 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
名称。您也可以插入多个%m
s - 如果您只想匹配类似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 匹配,那么它将被打印并用n
ext 输入行覆盖。如果新行匹配任何月份但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]