【问题标题】:Bash - fastest way to do whole string matching over array elements?Bash - 对数组元素进行整个字符串匹配的最快方法?
【发布时间】:2021-02-24 05:46:36
【问题描述】:

我的 bash 数组(下面称为 tenantlist_array)填充了以下格式的元素:

{3 characters}-{3-5 characters}{3-5 digits}-{2 chars}{1-2 digits}.

例子:

abc-hac101-bb0
xyz-b2blo97250-aa99
abc-b2b9912-xy00
fff-hac101-g3

数组元素是唯一的。请注意连字符,它是每个数组元素的一部分。

我需要检查提供的字符串(在下面的示例中用作变量tenant)是否产生与任何数组元素的完全匹配 - 因为数组元素是唯一的,所以第一个匹配就足够了。

我正在使用简单的代码迭代数组元素:

tenant="$1"

for k in "${tenantlist_array[@]}"; do
        result=$(grep -x -- "$tenant" <<<"$k")
        if [[ $result ]]; then
            break
        fi
done

请注意 - 我需要一个完整的字符串匹配 - 例如,如果我正在搜索的字符串是 hac101不得匹配任何数组元素,即使它可以是子字符串一个数组元素。

换句话说,只有完整的字符串abc-hac101-bb0 必须产生与第一个元素的匹配。字符串abcabc-hacb2b99- 不得产生匹配项。这就是 -x 参数与 grep 调用一起使用的原因。

现在,上面的代码可以工作了,但我发现它很慢。我使用具有 193 个元素的数组运行它,在普通笔记本上,遍历数组元素需要将近 90 秒:

real    1m2.541s
user    0m0.500s
sys     0m24.063s

数组中有 385 个元素,时间如下:

real    2m8.618s
user    0m0.906s
sys     0m48.094s

所以我的问题 - 有更快的方法吗?

【问题讨论】:

  • 如果你需要一个完整的字符串匹配,那么你不需要使用正则表达式(即使你这样做了,你也不需要产生一个子shell/使用 grep)。您应该能够简单地测试相等if [[ "$1" = "$k" ]]; then ...
  • @arco444 你是对的!问题 - 您是否看到任何潜在的缺点?
  • 不,我没有看到任何缺点 - 该操作实际上就是为此目的!

标签: arrays string bash search grep


【解决方案1】:

无需运行任何循环,您就可以使用 glob 执行此操作:

tenant="$1"

[[ $(printf '\3%s\3' "${tenantlist_array[@]}") == *$'\3'"$tenant"$'\3'* ]] &&
echo "ok" || echo "no"

printf 中,我们在每个元素周围放置一个控制字符\3,并在比较时确保在搜索键之前和之后放置\3

【讨论】:

  • 让我再次尝试解释一下,因为似乎存在误解。我已经有了搜索字符串——我不需要检查该字符串是否确认了对数组元素的要求。我需要检查该字符串是否与任何数组元素 1:1 匹配。
  • 我认为这个描述很清楚:'我需要检查提供的字符串是否与数组元素完全匹配。'
【解决方案2】:

感谢 @arco444,解决方案非常简单:

tenant="$1"

for k in "${tenantlist_array[@]}"; do
        if [[ $k = "$tenant" ]]; then
           result="$k"
        break
        fi
done

以及385个成员数组的种子差异:

real    0m0.007s
user    0m0.000s
sys     0m0.000s

快。

这让您了解调用 grep 有多么浪费,如果可能,需要避免这种情况。

【讨论】:

  • 代码中有错字:应该是==而不是=。此外,这种东西的 bash 真的很慢......考虑使用 awk 甚至更简单的 grep。
  • @RiccardoPetraglia 这不是错字。见stackoverflow.com/questions/20449543/…。也请仔细阅读问题。 OP 已经声明他们在bash 中有一个数组,并且在脚本中使用grep 会导致性能问题。
  • @arco444 感谢您向我指出有关=== 的解释,这非常有帮助!关于grep:他们的方法很慢,因为他们以完全错误的方式使用 grep。我将使用 grep 的方式添加答案。
【解决方案3】:

这是使用grep 的另一种方式,实际上是在最大程度上使用grep

第一次创建数组时,只需在每个 uuid 字符串的末尾附加 \n 即可完全删除“格式化”数组的代码。

随着比较字符串的长度和数组的长度,这段代码的降级也会慢得多。

tenant="$1"

formatted_array=""
for k in "${tenantlist_array[@]}"; do
        formatted_array="$formatted_array $i\n"
done

result=$(echo -e "$formatted_array" | grep $tenant)

【讨论】:

  • 这里有几个问题。 1. 仅出于构造字符串的目的而循环数组没有什么意义,因此您可以grep 它,您也可以在循环中检查所需的值并中断,就像 OP 已经在做的那样。 2. $result 将包含整个字符串,而不是所需的元素。如果您想要grep 解决方案,您可以跳过循环并说result=$(echo -e ${tenantlist_array[@]} | grep -o "$tenant")
  • @arco444 第 1 点可以。但是,正如我在回答中所说,您可以使用 \n 后缀构建数组,我认为我们都同意 @987654330 的性能这一事实@ 优于用 bash 比较字符串的性能。关于第2点,这可能是最好最快的解决方案。
  • 我当然不同意这种情况下的任何优越性能——即产生一个子shell来运行一个进程来检查一个已经在父进程内存中的变量的值。在没有 grep 的情况下进行检查几乎肯定会更快。无论如何,如果使用循环而不尽早退出它,任何卓越的性能仍然会完全丧失
  • 我说:“grep 的性能优于使用 bash 比较字符串的性能”。你可以做任何你喜欢的测试......它可能取决于字符串的长度和数组的长度但是......来吧......你真的不能争论这个。
  • “无论如何,使用循环而不是尽早退出它仍然会完全丧失任何卓越的性能”如果您指的是我的循环添加\n,这就是我说的原因以您建议的方式使用grep 是最好的解决方案。尽管如此,如果您可以在构建数组时添加\n,则性能将比将字符串与 bash 进行比较(对于“足够长”的字符串)更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 2020-08-22
  • 2021-02-24
  • 2017-03-29
相关资源
最近更新 更多