POSIX Shell脚本中AWK动态条件筛选SSH主机问题
POSIX Shell脚本主机筛选问题及解决
问题背景
我编写了一个POSIX Shell脚本,用于从包含SSH主机及可用内存、已连接用户等信息的列表中筛选合适主机。核心思路是根据用户选择的可选特征,在Shell变量中构建条件语句,再传入AWK提取符合条件的主机并随机选择。
原实现思路
构建筛选条件变量
FILTER变量示例如下:
FILTER='$6 <=MAXUSERS && $7 <=MAXUSERS' FILTER='$6 <=MAXUSERS && $7 <=MAXUSERS && $9 >=10' FILTER='$6 <=MAXUSERS && $7 <=MAXUSERS && $9 >=10 && $11 >=8'
(其中$X对应主机特征的固定位置参数)
通过setUpFilter函数动态构建条件:
setUpFilter(){ FILTER='$6 <=MAXUSERS && $7 <=MAXUSERS' if [ -n "$CPUNUM" ]; then FILTER="${FILTER} && \$9 >=$CPUNUM" fi if [ -n "$FREEMEM" ]; then FILTER="${FILTER} && \$12 >=$FREEMEM" fi }
原筛选逻辑
通过filterHosts函数筛选主机:
filterHosts(){ setUpFilter for HOST in $(echo "$AVAILABLEHOSTS"); do # $AVAILABLEHOSTS仅包含主机名列表 HOSTDATA=$(echo "$REPORTCLEAN" | egrep -A 11 "$HOST") # $REPORTCLEAN是从HTML提取的主机及数据列表 FILTEREDHOSTS=${FILTEREDHOSTS}$(echo $HOSTDATA | awk -v FILTER="$FILTER" -v MAXUSERS=5 '{ if (FILTER) print $1 "\\n" }') done }
问题现象
上述代码未按预期工作:筛选完全无效,所有主机都被存入$FILTEREDHOSTS,仿佛条件始终为true。但如果在AWK脚本内手动写入条件,筛选功能正常:
filterHosts(){ setUpFilter for HOST in $(echo "$AVAILABLEHOSTS"); do # $AVAILABLEHOSTS仅包含主机名列表 HOSTDATA=$(echo "$REPORTCLEAN" | egrep -A 11 "$HOST") # $REPORTCLEAN是从HTML提取的主机及数据列表 FILTEREDHOSTS=${FILTEREDHOSTS}$(echo $HOSTDATA | awk -v MAXUSERS=5 '{ if ($6 <=MAXUSERS && $7 <=MAXUSERS && $9 >=10) print $1 "\\n" }') done }
注:$HOSTDATA格式为HOSTNAME PARAM2 PARAM3 ... PARAM12,需匹配参数值提取$1(主机名)。
原方案失效原因
当通过-v FILTER="$FILTER"将条件字符串传入AWK时,AWK会将FILTER视为普通字符串变量,而非可执行的条件表达式。此时if (FILTER)仅判断字符串是否非空(显然构建后的FILTER不为空),因此永远返回true,不会解析执行字符串中的$6、MAXUSERS等变量和逻辑判断。
若要让AWK执行动态条件,虽可通过eval实现,但这种方式存在安全风险(比如注入恶意代码),并非最优解。
优化后的可用方案
将筛选逻辑直接移入AWK脚本,通过单独传递参数的方式构建条件,目前测试可用:
filterHosts(){ for HOST in $(echo "$AVAILABLEHOSTS"); do HOSTDATA=$(echo "$REPORTCLEAN" | egrep -A 11 "$HOST") FILTEREDHOSTS=${FILTEREDHOSTS}$(echo $HOSTDATA | awk -v cpunum="$CPUNUM" -v freemem="$FREEMEM" -v MAXUSERS="$1" '{ filter = $6 <=MAXUSERS && $7 <=MAXUSERS if (length(cpunum) != 0) filter = filter && $9 >= cpunum if (length(freemem) != 0) filter = filter && $12 >= freemem if (filter) print $1 "\\n" }') done }
内容的提问来源于stack exchange,提问作者Wyzed
相关产品推荐
相关产品推荐

