bash: перенаправить подоболочку на чтение

Немного истории за этим - я пытаюсь написать плагин nagios, чтобы определить, отключено ли монтирование nfs и не устарело ли монтирование, и именно здесь я столкнулся с проблемой.

Чего я пытаюсь добиться, так это определить, устарело ли крепление. Проблема, которую я пытаюсь решить, заключается в том, что устаревший дескриптор nfs приводит к зависанию любого действия в этом каталоге и тайм-ауту через 3-4 минуты. Принудительно установив тайм-аут для команды stat внутри смонтированного каталога nfs с чтением, я смогу обойти эту проблему.

Итак, я где-то взял этот фрагмент, который отлично работает при запуске вручную из cli на клиенте nfs (где /www/logs/foo — это устаревшее монтирование nfs)

$ read -t 2 < <(stat -t /www/logs/foo/*); echo $?
1

Проблема возникает, когда я пытаюсь включить этот фрагмент в такой скрипт (фрагмент прилагается, полный скрипт прилагается в конце):

list_of_mounts=$(grep nfs /etc/fstab | grep -v ^# | awk '{print $2'} | xargs)
exitstatus $LINENO

for X in $list_of_mounts; do
    AM_I_EXCLUDED=`echo " $* " | grep " $X " -q; echo $?`
    if [ "$AM_I_EXCLUDED" -eq "0" ]; then
    echo "" >> /dev/null
    #check to see if mount is mounted according to /proc/mounts
    elif [ ! `grep --quiet "$X " /proc/mounts; echo $?` -eq 0 ]; then
        #mount is not mounted at all, add to list to remount
        remount_list=`echo $remount_list $X`;
    #now make sure its not stale
    elif [ ! "`read -t 2  < <(stat -t $X/*) ; echo $?`" -eq "0" ]; then
        stalemount_list=`echo $stalemount_list $X`
    fi

Дает мне эту ошибку:

/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: syntax error near unexpected token `<'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: `read -t 2  < <( '
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: syntax error near unexpected token `)'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: ` ) ; echo $?'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: line 46: [: stat -t /www/logs/foo/*: integer expression expected

Мне удалось обойти синтаксическую ошибку, используя «read -t 2‹‹‹ $(stat -t $X/)» вместо «read -t 2‹ ‹(stat -t $X/)», однако stat больше не выигрывает от тайм-аута при чтении, что возвращает меня к исходной проблеме.

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

Полная проверка нагиос:

#!/bin/bash

usage() {
    echo "
    Usage:
    check_nfs_mounts.sh
    It just works.
    Optional: include an argument to exclude that mount point
"
}
ok() {
        echo "OK - $*"; exit 0
        exit
}
warning() {
        echo "WARNING - $*"; exit 1
        exit
}
critical() {
        echo "CRITICAL - $*"; exit 2
        exit
}
unknown() {
        echo "UNKNOWN - $*"; exit 3
        exit
}
exitstatus() {
        if [ ! "$?" -eq "0" ] ;
        then unknown "Plugin failure - exit code not OK - error line $*"
        fi
}
# Get Mounts
list_of_mounts=$(grep nfs /etc/fstab | grep -v ^# | awk '{print $2'} | xargs)
exitstatus $LINENO

for X in $list_of_mounts; do
    AM_I_EXCLUDED=`echo " $* " | grep " $X " -q; echo $?`
    if [ "$AM_I_EXCLUDED" -eq "0" ]; then
    echo "" >> /dev/null
    #check to see if mount is mounted according to /proc/mounts
    elif [ ! `grep --quiet "$X " /proc/mounts; echo $?` -eq 0 ]; then
        #mount is not mounted at all, add to list to remount
        remount_list=`echo $remount_list $X`;
    #now make sure its not stale
    elif [ ! "`read -t 2  <<< $(stat -t $X/*) ; echo $?`" -eq "0" ]; then
        stalemount_list=`echo $stalemount_list $X`
    fi
done
#Make sure result is a number
if [ -n "$remount_list" ] && [ -n "$stalemount_list" ];  then
    critical "Not mounted: $remount_list , Stale mounts: $stalemount_list"
elif [ -n "$remount_list" ] && [ -z "$stalemount_list"]; then
    critical "Not mounted: $remount_list"
elif [ -n "$stalemount_list" ] && [ -n "$remount_list" ]; then
    critical "Stale mount: $stalemount_list"
elif [ -z "$stalemount_list" ] && [ -z "$remount_list" ]; then
    ok "All mounts mounted"
fi

person user1466864    schedule 19.06.2012    source источник


Ответы (2)


Вам нужно убедиться, что ваш shebang указывает Bash:

#!/bin/bash

Причина сообщения об ошибке в том, что в вашей системе Bash имеет символическую ссылку на /bin/sh, которая используется, когда нет shebang или когда есть #!/bin/sh.

В этом случае Bash запускается так, как если бы вы запустили его с помощью bash --posix, который отключает некоторые функции, не относящиеся к POSIX, такие как подстановка процессов (<()), но, как это ни странно, не другие, такие как здесь строки (<<<).

Меняй шебанг и все будет ок.

person Dennis Williamson    schedule 20.06.2012
comment
Подтверждено, что это была просто проблема bash vs sh. - person user1466864; 03.07.2012

Вы можете сохранить вывод подоболочки следующим образом:

$ read a < <(echo one)
$ echo $a
one

Или таким образом (если вы просто хотите обработать $a и забыть об этом:

$ ( echo one; echo two) | (read a; echo $a)
one

Первый вариант будет работать только в bash. Bourne Shell (/bin/sh) не поддерживает этот синтаксис. Возможно, это причина, по которой вы получаете сообщение об ошибке. Может быть, ваш скрипт интерпретируется /bin/sh, а не /bin/bash

person Igor Chubin    schedule 19.06.2012