Пример скрипта bash

В этом посте я выложил пример скрипта написанного на bash и постарался подробно описать, что, как и почему делалось. На курсах Linux мы уже закончили тему bash и чтобы понять как мы ее усвоили преподаватель дал нам домашнее задание по написанию скрипта на bash. Задание звучало примерно так: “написать службу, которая будет выводить на 8-ю консоль время, в верхнем левом углу, с интервалом в одну секунду”.

Для решения задачи нам потребуется написать два скрипта. Первый скрипт будет выводить информацию на 8-ю консоль, а второй будет скриптом управления запуском-остановкой первого скрипта. Сначала рассмотрим более простой - первый скрипт. Я назвал его showtime.

Вот текст скрипта:

1
2
3
4
5
6
7
8
9
10
11
12
if  [ $(echo &>/dev/null > /dev/tty8; echo $?) -eq 0 ]
then
while [ 1 -lt 2 ]
do
    clear > /dev/tty8
    date +%H:%M:%S > /dev/tty8
    sleep 1
done
else
echo "Служба showtime. Управление через sudo /etc/init.d/timeconsoled start|stop"
exit 2
fi

Теперь комментарии. С первой строкой все понятно - это описание интерпретатора, который будет выполнять скрипт. Вторую строку - if [ $(echo &>/dev/null > /dev/tty8; echo $?) -eq 0 ] - разберем подробнее. Эта строка проверяет имеет ли скрипт доступ к консоли /dev/tty8, чтобы выводить туда информацию. Результат выполнения команд в $( ) сравнивается -eq с цифрой 0.

echo &>/dev/null > /dev/tty8 - пытаемся записать пустую строку в /dev/tty8. Чтобы на текущую консоль не выводились служебные сообщения команды echo (из потока вывода или потока ошибок), то мы перенаправляем их при помощи > в специальное устройство /dev/null (в “никуда”). После выполнения этой команды будет выполнена команда echo $?, которая вернет код результата выполнения последней команды. Если доступ к /dev/tty8, то будет возвращен 0 и скрипт продолжит выполнение перейдя на третью строку then, если писать не можем, то переходим на else (строка 12), затем на команду echo “Служба showtime. Управление через - sudo /etc/init.d/timeconsoled start|stop” (строка 13) и на завершающую команду exit 2 (строка 14). По команде exit 2 скрипт прекратит свою работу и вернет код возврата 2.

Если запись в /dev/tty8 возможна, то после второй строки попадаем на then (строка 3) и далее по порядку.

clear > /dev/tty8 - выполняем очистку консоли, если вдруг там был какая-то информация.
Строки 5, 6 и 11 (while do done) - организация цикла. while [ true ] - позволяет организовать бесконечный цикл, так как true всегда возвращает 0.
echo -en “\r \r” > /dev/tty8 - данная команда будет заменять область где будет выведено время пробелами и возвращать курсор в исходную позицию перед выводом следующего значения времени. Ключ n в команде echo сигнализирует что после вывода информации не нужно переходить на следующую строку. Ключ e - означает, что \r должно интерпретироваться как возврат каретки (курсора) на начало строки. То есть курсор будет возвращен на начало строки, затем будет выведена пустая строка и курсор снова будет возвращен на начало строки в консоли /dev/tty8.

ttt=$(date +%H:%M:%S) - присваиваем переменной ttt текущее значение времени.

echo -n “$ttt” > /dev/tty8 - выводим значение времени на восьмую консоль.
sleep 1 - останавливаем скрипт на одну секунду. Меняя значение можно организовать другой шаг обновления времени на консоли.

С первым скриптом все. Его уже можно попробовать запустить от имени рута и посмотреть выводится ли время на консоль. Не забудьте только сделать скрипт исполняемым: chmod a+x showtime.

1
2
igor@adm-ubuntu:~/linux$ sudo /usr/sbin/showtime &
[1] 12562

Переходим ко второму скрипту.

Этот скрипт будет находится в директории /etc/init.d/, где находятся все скрипты управления службами. Данный скрипт будет называться timeconsole, и в качестве параметров будет принимать стандартные start (для запуска) и stop (для остановки).

Текст скрипта timeconsole.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
case $1 in
start)
        if [ -z "$(ps ax | grep "/usr/sbin/[s]howtime")" ]
        then
                if [ -x /usr/sbin/showtime  ]
                then
                        echo "Запускаем демон..."
                        /usr/sbin/showtime &
                        sleep 1
                        if [ -n "$(ps ax | grep "/usr/sbin/[s]howtime")" ]
                        then
                                echo "Демон запущен"
                        else
                                echo "Запуск не удался!"
                                exit 1
                        fi
                else
                        echo "Исполняемый файл не найден!"
                fi
        else
                echo "Демон с именем /usr/sbin/showtime уже запущен!"
        fi
;;
stop)
        PID="$(ps ax | grep "/usr/sbin/[s]howtime" | awk '{ print $1 }')"
        if [ -n "$PID" ]
        then
                echo "Останавливаем демон..."
                kill -15 "$PID"
                sleep 1
                if [ -z "$(ps ax | grep "/usr/sbin/[s]howtime" | awk '{ print $1 }')" ]
                then
                        echo "Демон остановлен"
                        clear > /dev/tty8
                else
                        echo "Остановка не удалась"
                        exit 1
                fi
        else
                echo "Демон не запущен!"
        fi
;;
*)
        echo
        echo "Набирайте $0 start|stop"
        echo
        exit 1
;;
esac

Данный скрипт можно расширять почти до бесконечности всяческими проверками и служебными сообщениями, поэтому пока в таком варианте. Основа скрипта - case который во второй строке проверяет первый параметр передаваемый скрипту во время запуска. Реализована обработка параметров start и stop, а также сообщение-подсказка, если исполняют скрипт без параметра или с другими параметрами. Ничего сложного в данном скрипте (как и в первом) нет, поэтому остановлюсь только на некоторых командах. Если будут еще непонятные моменты, спрашивайте в комментариях - обязательно отвечу.

Строка 4 - if [ -z "$(ps ax | grep "/usr/sbin/[s]howtime”)” ] - здесь идет проверка не запущен ли уже процесс с именем /usr/sbin/showtime. Почему написано “/usr/sbin/[s]howtime”, а не “/usr/sbin/showtime”? Это особенность работы команды grep. Посмотрите на результаты работы обеих вариантов при запущенном процессе showtime:

1
2
3
igor@adm-ubuntu:~/linux$ ps ax | grep "/usr/sbin/showtime"
13276 pts/0    S      0:00 /bin/bash /usr/sbin/showtime
14375 pts/0    S+     0:00 grep /usr/sbin/showtime
1
2
igor@adm-ubuntu:~/linux$ ps ax | grep "/usr/sbin/[s]howtime"
13276 pts/0    S      0:00 /bin/bash /usr/sbin/showtime

А вот если процесс showtime не запущен:

1
2
igor@adm-ubuntu:~/linux$ ps ax | grep "/usr/sbin/showtime"
14375 pts/0    S+     0:00 grep /usr/sbin/showtime
1
igor@adm-ubuntu:~/linux$ ps ax | grep "/usr/sbin/[s]howtime"

Почему так - распишу в отдельной статье.

Строка 26 - PID=”$(ps ax | grep “/usr/sbin/[s]howtime” | awk ‘{ print $1 }’)” - команда получает PID запущенного процесса /usr/sbin/showtime, который затем будет использован для команды kill (строка 30). О команде awk я упоминал здесь.

Строка 46 - echo “Набирайте $0 start|stop” - $0 - вернет имя скрипта timeconsole в том виде в котором его запускали из консоли.

Для написания данных скриптов достаточно было информации о bash, которую я получил на лекциях посвященных bash, плюс man echo и анализ скриптов находящихся в каталоге /etc/init.d.

Теперь осталось скопировать скрипт showtime в каталог /usr/sbin/, а скрипт timeconsole в каталог /etc/init.d/.

В работе запуск и останов выглядит так:

1
2
3
4
5
6
7
8
9
10
igor@adm-ubuntu:~/linux$ sudo /etc/init.d/timeconsole start
Демон с именем /usr/sbin/showtime  уже запущен!

igor@adm-ubuntu:~/linux$ sudo /etc/init.d/timeconsole stop
Останавливаем демон...
Демон остановлен

igor@adm-ubuntu:~/linux$ sudo /etc/init.d/timeconsole start
Запускаем демон...
Демон запущен

Скрипты писались и проверялись в Ubuntu 9.10

Статьи и новости схожей тематики:

Комментариев: 3

  1. Max:

    День добрый.
    В скрипте showtime красивше использовать

    while true

    вместо

    while [ 1 -lt 2 ]

    Ответить

    Igorka Reply:

    Добрый день,
    Согласен. Спасибо за подсказку.

    Ответить

  2. SchiFFer:

    Как насчет заменить

    PID=”$(ps ax | grep “/usr/sbin/[s]howtime” | awk ‘{ print $1 }’)”

    на

    pgrep showtime

    ? :)

    К тому же, внутри скрипта можно вычитывать значение обратными кавычками PID=`pgrep showtime` (передал в переменную для примера, можно и сразу, без транзита :))

    Ответить

Оставьте свой отзыв