改进使用循环、awk和cut的BASH脚本的性能



这是一个bash脚本的中间片段,我们用来监视服务器上装载的状态:

OIFS=$IFS
IFS=$'n'
for mount in $mounts; do
        mountcount=$(($mountcount+1))
        dev=`echo $mount | awk {'print $1'};`
        dir=`echo $mount | awk {'print $2'};`
        opts=`echo $mount | awk {'print $4'};`
        state=`echo $opts | cut -d ',' -f 1`
        if [ "$state" = "ro" ]; then
                crit="true"
                break
        fi
done
IFS=$IFS

$mounts的内容类似于:

rootfs / rootfs rw 0 0
none /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
none /proc proc rw,nosuid,nodev,noexec,relatime 0 0
none /dev devtmpfs rw,relatime,size=1028136k,nr_inodes=218146,mode=755 0 0
none /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
/dev/disk/by-uuid/f2337686-ec8d-429a-9002-592c564ddbf3 / ext3 rw,relatime,errors=remount-ro,barrier=0,data=ordered 0 0
none /sys/kernel/debug debugfs rw,relatime 0 0
none /sys/kernel/security securityfs rw,relatime 0 0
none /dev/shm tmpfs rw,nosuid,nodev,relatime 0 0
none /var/run tmpfs rw,nosuid,relatime,mode=755 0 0
none /var/lock tmpfs rw,nosuid,nodev,noexec,relatime 0 0

正如您应该能够看到的,我正在进行解析,以将每一行拆分到它的组件中,寻找只读装载的装载。从功能上讲,这绝对可以正常工作,但问题是我们在100台服务器上运行它,目前(有时)需要一秒钟以上的时间才能用上面的数据运行完上面的循环。

我相信这个问题是由执行awkcut时的等待引起的,因为它们是外部程序,所以我想知道是否有更有效的方法可以实现相同的功能。我对bash不够精通,不知道可能有助于实现这一点的内部函数,或者对awk不够精通,无法将这一切作为一行代码来完成。

我的感觉是awk的3个呼叫和cut的1个呼叫都可以在awk的1行中实现。非常感谢您的帮助!

编辑

变量dev、dir和mountcount稍后将在脚本中用于构建输出。

编辑

我已经将脚本修改为以下内容:(所有的echo都在那里作为测试)

mountcount=0
OIFS=$IFS
IFS=$'n'
for mount in $mounts; do
    mountcount=$(($mountcount+1))
    echo $mount
    echo $mount | read dev dir fs opts
    echo $dev
    echo $dir
    echo $fs
    echo $opts
    state=`echo $opts | cut -d ',' -f 1`
    if [ "$state" = "ro" ]; then
        crit="true"
        break
    fi
done
IFS=$OIFS

这给了我以下信息:

rootfs / rootfs rw 0 0


fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0


/dev/disk/by-uuid/1be5b3ae-8239-4177-9af6-22ad0afa662a / ext3 rw,relatime,errors=remount-ro,data=ordered 0 0


/dev/disk/by-uuid/1be5b3ae-8239-4177-9af6-22ad0afa662a /dev/.static/dev ext3 rw,relatime,errors=remount-ro,data=ordered 0 0


devpts /dev/pts devpts rw,relatime 0 0


securityfs /sys/kernel/security securityfs rw,relatime 0 0

所以read并没有像预期的那样工作。

这可能对您有用:

OIFS=$IFS; IFS=$'n'; ma=($mounts); IFS=$OIFS
mountcount=0
for mount in "${ma[@]}"; do
    ((mountcount++))
    fa=($mount)
    dev=${fa[0]}
    dir=${fa[1]}
    opts=${fa[3]}
    state=${fa[3]/,*}
    if [ "$state" = "ro" ]; then
            crit="true"
            break
    fi
done

反转逻辑怎么样?一开始运行awk 3次,运行cut一次,将结果存储到数组$devs, $dirs, $optses, $states中。然后,在for ((i=0; i<max; i++))循环中,获取${devs[i]}等等,并对它们执行操作。

可能类似

read dev dir fs opts <<<"$mount"

对于初学者来说?

或者整件事看起来很像

read dev dir fs opts <<<"$(grep ' ro,' <<<"$mounts"|head -n 1)"

如果有临界线的话,它会给你一条临界线(也许grep的表达更精细会更好)。不算在内,在这种情况下。

附言:最后一行IFS=$IFS我相信你指的是IFS=$OIFS

也许你在这里缩写了你的脚本,里面有我看不到的更多。但是你为什么不直接做呢

echo "$mounts" | grep -w 'ro'

获取所有只读装载的列表,或者,如果您只想要第一个,

echo "$mounts" | grep -w 'ro' | head -1

你仍然可以用awk处理这个输出,但awk要做的事情要少得多,所以它应该运行得更快。

"ro"和-w对于任务来说应该足够独特,但如果出现误报,则可以使用具有更复杂模式的egrp。

最新更新