Code/module 计算要在 awk 中使用的布尔表达式或 bash on Linux
Code/module to evaluate a Boolean expression to be used in awk or bash on Linux
我正在研究一些 bash 使用 awk 脚本来搜索许多文件和 return 匹配各种条件组合的各种行组合。
我想要一些方法来为我的脚本添加能够处理简单但通用的布尔 "query" 表达式的功能 - 例如:
A AND (B OR C) AND NOT D
其中 A、B、C 和 D 将是 bash 或 awk 变量,其值类似于 TRUE=0
和 FALSE!=0
或相反。
我的代码会设置每个变量,然后将使用这些变量的布尔表达式传递给可以对其求值的对象。
我真的不需要像嵌套括号这样花哨的东西,但一级确实有帮助。我不需要大量的表达式错误 checking/recovery。如果我写了一个错误的表达式,那是我的问题,只要它不会用 bash/awk 语法错误之类的东西完全杀死我的脚本的其余部分。
解决方案可以是 bash 或 awk 的本地解决方案,也可以是从 return 真或假 (0/1) 的任一语言中调用的外部程序。
我知道如何用 if 语句等硬编码这些东西,但我需要能够为每次搜索更改条件,所以不能硬编码。
我绝对不知道用 lex/yacc.
之类的东西来滚动我自己的解析器
sed
、find
和 grep
之类的工具很棒,但是(至少就其自身而言),它们无法满足我的要求并且对语法过于挑剔用于编写临时查询。
作为一种替代方案,我正在考虑尝试将我的查询转换为简单的数学并将它们通过 (( query ))
放入 bash - 例如对于上面的示例(没有使用完全防御的语法使示例变得丑陋,并且所有变量都设置为 1 表示真,0 表示假。)
(( $A * ($B + $C) * (! $D) ))
我不完全明白你的目标是什么,但我创建了一个小的 bash script to get any boolean expression and write its truth-table. Maybe this helps. It process any braces, AND, OR and NOT expressions. Variable is anything which starts with a letter (on the language you set). The only thing has to be changed is the COND
variable. It can be set from the command line as well. It works only if your bash 支持关联数组 (declare -A
)。
这是脚本:
#!/usr/bin/bash
COND="A AND (B OR C) AND NOT DD"
# Convert to bash syntax
cond=${COND// AND / && }
cond=${cond// OR / || }
cond=${cond// NOT / ! }
cond=${cond//^NOT /! }
cond=${cond//(/ ( }
cond=${cond//)/ ) }
# Now $cond can be used in eval adding "((..))" around it.
# The rest of the script prints the complete truth-table
# Collect unique variables
declare -A hvars
for var in $cond; do
[[ $var =~ ^[[:alpha:]] ]] && hvars[$var]=1
done
# Make vars array
vars=(${!hvars[@]})
# Number of variables
n=${#vars[@]}
# Number or rows in truth-table
((N=1<<n))
echo "${vars[@]} | $COND"
for ((i=0; i<N;++i)); do
for ((b=0; b<n; ++b)) do
var=${vars[b]}
((val=i & 1<<(n-1-b) ? 1 : 0))
printf "%*d " ${#var} $val
eval "(($var=$val))"
done
eval "((r=$cond))"
echo "| $r";
done
输出:
A B C DD | A AND (B OR C) AND NOT DD
0 0 0 0 | 0
0 0 0 1 | 0
0 0 1 0 | 0
0 0 1 1 | 0
0 1 0 0 | 0
0 1 0 1 | 0
0 1 1 0 | 0
0 1 1 1 | 0
1 0 0 0 | 0
1 0 0 1 | 0
1 0 1 0 | 1
1 0 1 1 | 0
1 1 0 0 | 1
1 1 0 1 | 0
1 1 1 0 | 1
1 1 1 1 | 0
它将人类可读格式的条件转换为bash格式(AND
到&&
,OR
到||
,NOT
到 !
和 (
和 )
周围设置一个 space。需要不要在 eval
中滥用 ))
。)然后它收集所有唯一变量。然后计算真值的大小-table。然后为每一行做一个循环。在该循环内,它创建了一个嵌套循环来计算每个变量的值并设置该值。主循环结束时计算表达式的值。
它不是防弹的。不要在变量名中使用特殊变量(DD=XX
或 A'B
)。
您的 bash 代码也设置了使用的变量,然后在 eval
中调用转换后的条件(参见 cond
)(参见最后的 eval
)条件适当。然后 $r
变量可以用于您自己的目的...
希望对您有所帮助!
我正在研究一些 bash 使用 awk 脚本来搜索许多文件和 return 匹配各种条件组合的各种行组合。
我想要一些方法来为我的脚本添加能够处理简单但通用的布尔 "query" 表达式的功能 - 例如:
A AND (B OR C) AND NOT D
其中 A、B、C 和 D 将是 bash 或 awk 变量,其值类似于 TRUE=0
和 FALSE!=0
或相反。
我的代码会设置每个变量,然后将使用这些变量的布尔表达式传递给可以对其求值的对象。
我真的不需要像嵌套括号这样花哨的东西,但一级确实有帮助。我不需要大量的表达式错误 checking/recovery。如果我写了一个错误的表达式,那是我的问题,只要它不会用 bash/awk 语法错误之类的东西完全杀死我的脚本的其余部分。
解决方案可以是 bash 或 awk 的本地解决方案,也可以是从 return 真或假 (0/1) 的任一语言中调用的外部程序。
我知道如何用 if 语句等硬编码这些东西,但我需要能够为每次搜索更改条件,所以不能硬编码。
我绝对不知道用 lex/yacc.
之类的东西来滚动我自己的解析器sed
、find
和 grep
之类的工具很棒,但是(至少就其自身而言),它们无法满足我的要求并且对语法过于挑剔用于编写临时查询。
作为一种替代方案,我正在考虑尝试将我的查询转换为简单的数学并将它们通过 (( query ))
放入 bash - 例如对于上面的示例(没有使用完全防御的语法使示例变得丑陋,并且所有变量都设置为 1 表示真,0 表示假。)
(( $A * ($B + $C) * (! $D) ))
我不完全明白你的目标是什么,但我创建了一个小的 bash script to get any boolean expression and write its truth-table. Maybe this helps. It process any braces, AND, OR and NOT expressions. Variable is anything which starts with a letter (on the language you set). The only thing has to be changed is the COND
variable. It can be set from the command line as well. It works only if your bash 支持关联数组 (declare -A
)。
这是脚本:
#!/usr/bin/bash
COND="A AND (B OR C) AND NOT DD"
# Convert to bash syntax
cond=${COND// AND / && }
cond=${cond// OR / || }
cond=${cond// NOT / ! }
cond=${cond//^NOT /! }
cond=${cond//(/ ( }
cond=${cond//)/ ) }
# Now $cond can be used in eval adding "((..))" around it.
# The rest of the script prints the complete truth-table
# Collect unique variables
declare -A hvars
for var in $cond; do
[[ $var =~ ^[[:alpha:]] ]] && hvars[$var]=1
done
# Make vars array
vars=(${!hvars[@]})
# Number of variables
n=${#vars[@]}
# Number or rows in truth-table
((N=1<<n))
echo "${vars[@]} | $COND"
for ((i=0; i<N;++i)); do
for ((b=0; b<n; ++b)) do
var=${vars[b]}
((val=i & 1<<(n-1-b) ? 1 : 0))
printf "%*d " ${#var} $val
eval "(($var=$val))"
done
eval "((r=$cond))"
echo "| $r";
done
输出:
A B C DD | A AND (B OR C) AND NOT DD
0 0 0 0 | 0
0 0 0 1 | 0
0 0 1 0 | 0
0 0 1 1 | 0
0 1 0 0 | 0
0 1 0 1 | 0
0 1 1 0 | 0
0 1 1 1 | 0
1 0 0 0 | 0
1 0 0 1 | 0
1 0 1 0 | 1
1 0 1 1 | 0
1 1 0 0 | 1
1 1 0 1 | 0
1 1 1 0 | 1
1 1 1 1 | 0
它将人类可读格式的条件转换为bash格式(AND
到&&
,OR
到||
,NOT
到 !
和 (
和 )
周围设置一个 space。需要不要在 eval
中滥用 ))
。)然后它收集所有唯一变量。然后计算真值的大小-table。然后为每一行做一个循环。在该循环内,它创建了一个嵌套循环来计算每个变量的值并设置该值。主循环结束时计算表达式的值。
它不是防弹的。不要在变量名中使用特殊变量(DD=XX
或 A'B
)。
您的 bash 代码也设置了使用的变量,然后在 eval
中调用转换后的条件(参见 cond
)(参见最后的 eval
)条件适当。然后 $r
变量可以用于您自己的目的...
希望对您有所帮助!