Улучшение производительности в скрипте BASH, который использует цикл, awk и вырезать - PullRequest
0 голосов
/ 13 января 2012

Это фрагмент скрипта 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

Как вы должны видеть, я разбираю каждую строку на части, чтобы найти компоненты, которые монтируются только для чтения. Функционально это работает абсолютно нормально, однако проблема в том, что мы выполняем это на сотнях серверов, и в настоящее время требуется более секунды (иногда) для прохождения вышеуказанного цикла с вышеуказанными данными.

Я полагаю, что эта проблема вызвана ожиданием при выполнении awk и cut, поскольку они являются внешними программами, поэтому мне было интересно, если есть более эффективный способ, с помощью которого можно было бы выполнить ту же функцию. Я не достаточно опытен в bash, чтобы знать внутренние функции, которые могли бы помочь с этим, или достаточно опытен в awk, чтобы делать все это одной строкой.

Мне кажется, что 3 вызова awk и 1 вызов cut могут быть выполнены в 1 строке awk. Любая помощь с благодарностью!

EDIT

Переменные dev, dir и mountcount используются позже в скрипте для построения вывода.

EDIT

Я изменил сценарий следующим образом: (Все эхо-тесты включены в качестве теста)

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 работает не совсем так, как ожидалось.

Ответы [ 4 ]

6 голосов
/ 13 января 2012

Это может работать для вас:

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
0 голосов
/ 20 декабря 2013

Возможно, вы сократили свой сценарий здесь, и в нем есть больше, чем я могу видеть.Но почему бы вам просто не набрать

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

, чтобы получить список всех монтируемых только для чтения монтировок, или, если вам нужна только первая,

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

?

Вы можете обработать вывод этого с помощью awk, но awk будет делать гораздо меньше, поэтому он должен работать значительно быстрее.

'ro' вместе с -w должно быть достаточно уникальным для задачи, но вы можете использовать egrep с более сложным шаблоном, если вы получаете ложные срабатывания.

0 голосов
/ 13 января 2012

А как насчет инверсии логики? Выполните awk 3 раза в начале и cut один раз, сохраните результаты в массивах $devs, $dirs, $optses, $states. Затем в цикле for ((i=0; i<max; i++)) получите ${devs[i]} и т. Д. И выполните работу с ними.

0 голосов
/ 13 января 2012

может быть что-то вроде

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

для начинающих?

Или все это выглядит очень похоже на

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

Это даст вам критическую линию, если естьэто один (может быть, было бы неплохо более сложное выражение для grep).Не считая, в этом случае.

PS В последней строке IFS=$IFS Я полагаю, вы имели в виду IFS=$OIFS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...