0

我正在以形式编写脚本

while read LINE 
do
    [[ $LINE =~ ^headertag1 ]] && function1 && continue
    [[ $LINE =~ ^headertag2 ]] && function2 && continue
    ...
done < filename

随着标签数量的增加,我每行都会做太多的检查。我可以尝试将常见标签排序更高,但我认为它不能解决根本问题。我不是软件工程师。有没有可以改善这种情况的编程概念/方法?

4

3 回答 3

1

是的,对于两个,您可以首先找到两者的最长公共前缀(这里人们想知道如何在 Bash 中做到这一点Longest common prefix of two strings in bash),然后首先检查行是否以它开头,然后在剥离它之后标签和行都检查行是否以其余部分开头。

对于两个以上,您需要创建一个 trie — 也称为前缀树https://en.wikipedia.org/wiki/Trie

那篇维基百科文章说

有关前缀树的空间优化表示,请参阅紧凑前缀树

并且拥有最长的公共前缀,这就是你将拥有的。

由于Bash 没有多维关联数组,您将不得不考虑https://en.wikipedia.org/wiki/Trie#Implementation_strategies或嵌入其他一些脚本语言,如 Perl 或 Python — 或 GNU Awk ( gawk),其中与标准 awk 不同,它引入了多维关联数组

使用 Bash 的关联数组实现的优化

正如评论中所建议的那样,我们可以考虑只使用带有更简单正则表达式的标签并将其用作在 Bash 中进行了一些优化的关联数组的键(我们可以在源代码中调查是否适合我们的需求:

如果我们知道它是由什么分隔的——比如,如果我们知道它后面总是紧跟着 a:或某物而不包含它,并使用更简单的正则表达式,例如:

[[ $LINE =~ ^(.*): ]] && "${DICTIONARY_OF_FUNCTIONS["${BASH_REMATCH[1]}"]}"

或者使用 Bash 的函数存储优化

如果您的所有标签都像,/[a-z][a-z0-9]+/或者被 Bash 接受为函数名称,并且在使用 Bash 关联数组的方法中进行分隔,那么您可以使用上述方法来插入函数名称,例如,

function the_function_for_tag_headertag1() {
    echo "hey it's the first one"
}
[[ $LINE =! ^(.*): ]] && {
    func_name="the_function_for_tag_${BASH_REMATCH[1]}"
    type "${func_name}" && "${func_name}"
}
于 2019-11-08T09:28:44.410 回答
1

您对每个标签执行的测试

    [[ $LINE =~ ^headertag1 ]] && function1 && continue

非常便宜(在内存正则表达式中。很可能,它会花费与读取 LINE(从文件或其他进程)相关的 IO 时间的一小部分。除非您执行大量测试,否则此实现是合理的。

关于样式的注意事项: 如果所有模式都是前缀匹配(或其他简单结构),请考虑使用bashcase 语句

case "$LINE" in
   header1*) function1 ;;
   header2*) function2 ;;
   ...
esac

这将使代码更优雅,但不会改变性能——RE 和通配符都很简单。

于 2019-11-08T09:35:59.230 回答
1

不确定这里,但是如果你想整理你的代码并且通过重复添加这些 if 守卫感到无聊,那么这个想法可能会有所帮助:

#!/bin/bash

tags[tag1]="some regex1"
tags[tag2]="some regex2"
tags[tag3]="some regex3"

function action() {
  echo "perl -pe '${tags[$tag]} other-file.txt'"
}

while read LINE; do
  for tag in "${!tags[@]}"; do
    [[ $LINE =~ ^$tag ]] && action "${tags[$tag]}"
  done
done < filename

不确定OP是否在问这样的问题。

于 2019-11-08T09:42:07.930 回答