25 марта 2011 г.

Красивое использование косвенной адресации в bash-скриптах

Косвенная адресация это когда, грубо говоря, одна переменная ссылается на какое-либо значение через другую переменную. Если говорить точнее и применительно к bash'у, то это когда одна переменная хранит имя другой переменной, которая в свою очередь содержит, например, какое-либо значение. С помощью косвенной адресации можно, используя первую переменную, сразу получить доступ к значению, которое содержит вторая переменная. Как это работает вроде бы ясно, но, возможно, не сразу понятна нужность всего этого.

Итак, допустим у нас есть переменная X, которая содержит строку "значение". Так же есть переменная Y, которая в качестве значения содержит имя переменной X. Красивость заключается в том, что с помощью прямой и косвенной адресации, используя переменную Y, можно получить соответственно имя и значение переменной X.

Простой пример:
X="значение"
Y=X
echo "$Y: ${!Y}"
В этом примере с помощью прямой адресации $Y мы получаем доступ к имени переменной X, а с помощью косвенной ${!Y} - к её значению. В старых версиях bash косвенная адресация могла использоваться только так eval Z=\$$Y, а начиная со 2 версии она стала выглядеть проще, хотя старый вариант так же может использоваться.

Теперь приведу более жизненный пример. У меня есть скрипт, который настраивает iptables. Косвенная адресация в нем используется для того, чтобы сделать проще изменение настроек скрипта и создать красивый вывод. Приведу пример поясняющий основную суть.
#!/bin/bash

# список сервисов и их портов
SSH=22
HTTP=80
HTTPS=443
SAMBA="137 138 139 445"

# локальные сервисы на которые разрешен доступ снаружи
INPUT_PORTS_ACCEPT="SSH SAMBA HTTP HTTPS"

echo "Удаленный доступ к сервисам хоста:"
# открываем доступ на разрешенные сервисы
for SERVICE_NAME in $INPUT_PORTS_ACCEPT
do
    # для каждого порта сервиса добавляем разрешающие правила в iptables
    PORTS=${!SERVICE_NAME} # устаревший вариант: eval PORTS=\$$SERVICE_NAME
    for PORT in $PORTS
    do
        iptables -t filter -A INPUT -i eth0 -p tcp --dport $PORT -j ACCEPT
        iptables -t filter -A INPUT -i eth0 -p udp --dport $PORT -j ACCEPT
    done

    # выводим имя и порты сервиса
    echo "$SERVICE_NAME: $PORTS --accept"
done
Вывод скрипта:

Удаленный доступ к сервисам хоста:
SSH: 22 --accept
SAMBA: 137 138 139 445 --accept
HTTP: 80 --accept
HTTPS: 443 --accept

Таким образом, для настройки скрипта в переменной INPUT_PORTS_ACCEPT нужно указывать не номера портов, а просто имена сервисов, и в выводе наглядно показано на какие локальные сервисы открыт доступ и какие порты им соответствуют. Красиво :)

1 комментарий:

  1. Как на счет Bash'евских ассоциатывных массивов? Так:

    declare -A arr
    arr[HTTP]='80 443'
    arr[SSH]=22
    for i in ${!arr[@]}; do echo $i = ${arr[$i]}; done

    Главное что ключи будут спрятаны в пространстве имен arr ))

    ОтветитьУдалить