首页 > 解决方案 > awk 退出的意外行为

问题描述

我有下一个代码:

process_mem() {
    used=`sed -n -e '/^Cpu(s):/p' $temp_data_file | awk '{print $2}' | sed 's/\%us,//'`
    idle=`sed -n -e '/^Cpu(s):/p' $temp_data_file | awk '{print $5}' | sed 's/\%id,//'`

    awk -v used=$used \
        -v custom_cpu_thres=$custom_cpu_thres \
        '{
            if(used>custom_cpu_thres){
                exit 1
            }else{
                exit 0
            }

        }'
    return=$?
    echo $return

    if [[ $return -eq 1 ]]; then
        echo $server_name"- High CPU Usage (Used:"$used".Idle:"$idle"). "
        out=1
    else 
        echo $server_name"- Normal CPU Usage (Used:"$used".Idle:"$idle"). "
    fi
}

while IFS='' read -r line || [[ -n "$line" ]]; do

    server_name=`echo $line | awk '{print $1}'`
    custom_cpu_thres=`echo $line | awk '{print $3}'`
    if [ "$custom_cpu_thres" = "-" ]; then
        custom_cpu_thres=$def_cpu_thres
    fi

    expect -f "$EXPECT_SCRIPT" "$command" >/dev/null 2>&1
    result=$?

    if [[ $result -eq 0 ]]; then
        process_mem 
    else 
        echo $server_name"- Error in Expect Script. "
        out=1
    fi
    echo $server_name
done < $conf_file

exit $out

问题是读取 bash 循环应该执行 4 次(每行读取一次)。但是,如果我编写带有退出的 awk 代码,则在第一个循环后读取 bash 循环退出。

为什么会这样?在我看来,awk 代码中的退出代码不应该影响 bash 脚本。

问候。

标签: bashshellawkscriptingexit

解决方案


我相信你的说法是错误的。

你说:

问题是读取 bash 循环应该执行 4 次(每行读取一次)。但是,如果我编写带有退出的 awk 代码,则读取 bash 循环在第一个循环之后退出。

我不相信脚本在第一个循环后退出,而是卡在第一个循环中。我发表此声明的原因是您的awk脚本有缺陷。你写的方式是:

awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
    '{ if(used>custom_cpu_thres){ exit 1 }
       else{ exit 0 } }'

这里的问题是 Awk 没有得到输入文件。如果没有输入文件被 awk 证明,它正在读取stdin(类似于处理管道或键盘输入)。由于没有信息发送到标准输入(除非您按下了几个键并意外点击Enter),因此脚本不会向前移动,并且 Awk 正在等待输入。

仅当未指定文件操作数,或文件操作数为 '-' ,或 progfile 选项参数为 '-' 时,才应使用标准输入;请参阅输入文件部分。如果 awk 程序不包含任何动作和模式,但它是一个有效的 awk 程序,则不应读取标准输入和任何文件操作数,并且 awk 将退出并返回状态为零。

来源:Awk POSIX 标准

以下 bash 行演示了上述语句:

$ while true; do awk '{print "woot!"; exit }'; done

只有当您按一些键后跟Enter,单词“woot!” 打印在屏幕上!

如何解决您的问题: 使用 awk 解决问题的最简单方法是使用BEGIN块。该块在读取任何输入行(或stdin)之前执行。如果您告诉 Awk 在开始块中退出,它将终止 Awk 而不读取任何输入。因此:

awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
    'BEGIN{ if(used>custom_cpu_thres){ exit 1 }
            else{ exit 0 } }'

或更短

awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
    'BEGIN{ exit (used>custom_cpu_thres) }

但是,在这里,Awk 有点过头了。一个简单的 bash 测试就足够了:

[[ "$used" -le "$custom_cpu_thres" ]]
result=$?

或者

(( used <= custom_cpu_thres ))
result=$?

推荐阅读