我有一个相当长的 case 语句(大约需要匹配 150 个模式)。
case "$line" in
pattern1) ret=result1;;
pattern2) ret=result2;;
pattern3) ret=result3;;
...
...
esac
但是,这并不是很合适,因为模式实际上是用户可修改的数据。因此,每次需要新模式时,我都需要进入并修改 case 语句。
我想做的是创建一个单独的文本文件,例如:
pattern1=result1
pattern2=result2
pattern3=result3
...
...
文本文件将被读取并用于创建 case 语句。这样做的优点是不需要每次需要新模式时都修改代码。
有没有办法根据文本文件的输入自动创建 case 语句?我对其他选项持开放态度,但我正在寻找一种可以非常快速地对输入行进行模式匹配的解决方案,因为我还有大量的输入行需要匹配。
答案1
动态 case 语句生成是一件相当奇怪的事情……但是,是的,这是可能的。
我会这样做,如下所示(如果我探索过全部我能想到的其他方法;我不知道我会推荐这个):
. <( awk -F= 'BEGIN { print "case \"$line\" in" }
{ print $1 ") ret=" $2 ";;" }
END { print "esac" }' patternfile )
如果patternfile
包含
pattern1=result1
pattern2=result2
pattern3=result3
该awk
命令本身会输出
case "$line" in
pattern1) ret=result1;;
pattern2) ret=result2;;
pattern3) ret=result3;;
esac
通过该. <( awk ... )
构造,这将被源到您的脚本中,就像您直接在脚本文件中编写大小写开关一样。
这回答了 shell 脚本中的“动态 case 语句创建”。但是,既然您打算这样做在一个循环内,上面的方法将是一个非常糟糕的方法 - 因为您将awk
在 上运行 10,000 次patternfile
,从而超过任何可能的性能优势。
相反,最好编写代码生成器来创建案例切换在函数定义内—创建并获取整个函数定义,换句话说:
# At the top of your script file:
. <( awk -F= 'BEGIN { print "my_case_switch_function() {"
print "case \"$1\" in" }
{ print $1 ") ret=" $2 ";;" }
END { print "esac"
print "}" }' patternfile )
# Within your while loop (or wherever else you want):
my_case_switch_function "$line"
case
这将允许您在仅处理一次后一遍又一遍地重复使用生成的开关(如果您愿意,可以重复使用 10,000 次) patternfile
。因此,性能与您手动创建大小写开关patternfile
并将其硬编码到脚本中一样好(除了awk
在 150 行文件上运行一次的小开销之外,这与处理 10,000 行文件相比可以忽略不计) )。
但是,必须重申:shell 脚本大小写开关是不是用于逐行处理 10,000 行文件的工具。因此,尽管此解决方案非常接近通过硬编码大小写开关获得的性能,但它可能仍然很慢。
去引用斯蒂芬·查泽拉斯:
如前所述,运行一个命令是有成本的。如果该命令不是内置的,则成本巨大,但即使它们是内置的,成本也很大。
shell 并不是被设计成这样运行的,它们没有自称是高性能的编程语言。它们不是,它们只是命令行解释器。因此,在这方面几乎没有进行任何优化。
答案2
我认为这是一个 XY问题。您实际上并不需要生成动态case
语句。您只想使用文件作为简单的键值存储。一种解决方案是使用 搜索文件grep
,然后使用 提取值cut
:
ret=$(grep "^$line" file.txt | cut -d = -f 2)
答案3
使用此模式文件:
a*c=abc
def=def
=
并假设每行只有一个:
#! /bin/bash
line=abc
while IFS= read -r patternline; do
[[ $patternline =~ .=. ]] || continue
pattern="${patternline%=*}"
result="${patternline##*=}"
if [[ "$line" == $pattern ]]; then
ret="$result"
echo "$patternline"
break
fi
done <patterns.txt
如果必须检查多行,则文件内容将被读入数组:
#! /bin/bash
patterns_file="patterns.txt"
patterns=()
results=()
patternindex=0
### BEGIN: init
while IFS= read -r patternline; do
[[ $patternline =~ .=. ]] || continue
patterns[patternindex]="${patternline%=*}"
results[patternindex]="${patternline##*=}"
((patternindex++))
done <"$patterns_file"
pattern_count="$patternindex"
### END: init
patterncheck () {
local line="$1" i=0 pattern= result=
for((i=0;i<pattern_count;i++)); do
pattern="${patterns[i]}"
result="${results[i]}"
if [[ "$line" == $pattern ]]; then
ret="$result"
echo "$line"
break
fi
done
} # patterncheck ()
# tests
patterncheck abc
patterncheck def