上下文:我正在编写一个shell脚本来帮助管理以文本文件中以人类可读方式存储的简单数据库,并使用普通文本编辑器进行编辑。 (每个条目都是一个文本文件,其名称是一个ID号,所有文件都存储在一个目录中。)
我目前的问题是搜索。有一些标题,它们基本上是文件顶部的数据字段。例如,让我们使用tags字段,该字段从Tags:\t
的新行开始(其中\t
是文字制表符),然后有一个以逗号分隔的标记列表。我希望能够将用户提供的正则表达式插入到grep
的较大调用中,并且仅在每个逗号分隔项中使用户的正则表达式匹配。
我的文档中有一点描述了我想要发生的事情:
hregexes 仅在逗号分隔的项目中匹配ERE。例如,标题为Tags: foo, bar baz
:
REGEX :: MATCHES?
foo :: yes
bar :: yes
baz :: yes
az :: yes
.*baz :: yes
ba.*az :: yes
o, ba :: no
foo.*baz :: no
理想情况下,这将完全与POSIX扩展正则表达式一起使用,以与系统的其余部分保持一致;我有一个简化版本的搜索工作在Python但决定我应该重写该部分,以便系统不会有一些搜索采用POSIX正则表达式和一些Python。
我确实试图想出一个模式,但是我用regexp来做这个复杂的事情并不是很好。在以下尝试中,$2
是我们要查找的标头,$3
是该标头中匹配的模式。
grep -El "$2: (|.*,|.*, )[^,]*$3[^,]*(,|\b)" *.dre
这不会错过它应该捕获的任何结果,但它存在o, ba
和foo.*baz
都不匹配的问题;此时我不妨只搜索$2: .*$3
。
如果使用单个ERE无法做到这一点,那还有另一种在Bash中执行此操作的好方法吗?我的数据库已经有超过一千个文件,很容易增长到很多次,所以我不想循环遍历每个文件,然后遍历逗号分隔列表中的每个项目,每次都会产生shell开销。
答案 0 :(得分:1)
诀窍是将逗号更改为更好的作为grep中的分隔符,即换行符。
head -1 $DATA_FILE | sed -E 's/,/\'$'\n/g' | grep -qE "$SEARCH"
if [ $? == 0 ]
then
echo "Pattern found: $DATA_FILE"
else
echo "Pattern not found: $DATA_FILE"
fi
$DATA_FILE
是包含标签的文件。
$SEARCH
是正在寻找的正则表达式。
显然if
语句将替换为适合您的应用程序的逻辑。
head
命令从文件中提取第一行(“Tag:”行)。
sed
命令用换行替换该行上的任何逗号(此时删除“Tag:”以避免误报可能是明智的)。
grep
然后只需搜索输入正则表达式的每个结果行集并返回指示是否找到它的状态。
搜索每个数据文件的最小数量。
答案 1 :(得分:1)
以下解决方案基于Perry改变分离器的想法,并非万无一失,但保留了理想的运行时间,同时又很难搞砸。
首先,我们选择一个分隔符来替换逗号;我选择@@@@@
,推断在任何正确形成的标签中都不会出现这种情况。 (标签通常纯粹是字母数字。)
然后,我们修改用户的正则表达式,将.
替换为[^@]
,这样除非明确编写,否则任何表达式都不会越过@@@@@
边界。我可能会错过其他一些比赛,比如[[:punct:]]
;我并不十分担心这些,但如果有人想到其他可能有问题的特殊角色,我想听听它们。
最后,我们创建一个包含所有Tags
行的流,编辑它以仅包含文件名和新的@
- 分隔标记,将用户的模式匹配应用于此流,以及然后从匹配流中删除除文件名之外的所有内容。
最终代码:
header="$2"
pattern=$(echo "$3" | sed -e 's/\./[^@]/')
grep -m 1 "$header: " *.dre | sed -e "s/$header: //" | \
sed -e 's/, /@@@@@/g' | grep -E "$pattern" | \
sed -e 's/\([0-9]\{5\}\.dre\):.*/\1/'
([0-9]\{5\}\.dre
是一个匹配所有合法文件名的表达式。)
示例输出:
00775.dre
00787.dre
00788.dre
00883.dre
00889.dre
(显然,匹配可以保存到变量中以便进一步处理;这就是我在这里所做的。)