Глава 2. Для начала о Sha-Bang


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

Пример 2-1. cleanup: Сценарий очистки лог-файлов в /var/log

  1. # cleanup
  2. # Для работы сценария требуются права root.
  3. cd /var/log
  4. cat /dev/null > messages
  5. cat /dev/null > wtmp
  6. echo "Лог-файлы очищены."

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

Пример 2-2. cleanup: Расширенная версия предыдущего сценария.

  1. #!/bin/bash
  2. # cleanup, version 2
  3. # Для работы сценария требуются права root.
  4. LOG_DIR=/var/log
  5. ROOT_UID=0     # Только пользователь с $UID 0 имеет привилегии root.
  6. LINES=50       # Количество сохраняемых строк по-умолчанию.
  7. E_XCD=66       # Невозможно сменить каталог?
  8. E_NOTROOT=67   # Признак отсутствия root-привилегий.
  9. if [ "$UID" -ne "$ROOT_UID" ]
  10. then
  11.   echo "Для работы сценария требуются права root."
  12.   exit $E_NOTROOT
  13. fi
  14. if [ -n "$1" ]
  15. # Проверка наличия аргумента командной строки.
  16. then
  17.   lines=$1
  18. else
  19.   lines=$LINES # Значение по-умолчанию, если число не задано в командной строке
  20. fi
  21. #  Stephane Chazelas предложил следующее,
  22. #+ для проверки корректности аргумента, переданного из командной строки,
  23. #+ правда это достаточно сложно для данного руководства.
  24. #
  25. #    E_WRONGARGS=65  # Не числовой аргумент
  26. #
  27. #    case "$1" in
  28. #    ""      ) lines=50;;
  29. #    *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
  30. #    *       ) lines=$1;;
  31. #    esac
  32. #
  33. #* Конец проверки корректности аргумента
  34. cd $LOG_DIR
  35. if [ `pwd` != "$LOG_DIR" ]  # или   if [ "$PWD" != "$LOG_DIR" ]
  36.                             # Не в /var/log?
  37. then
  38.   echo "Невозможно перейти в каталог $LOG_DIR."
  39.   exit $E_XCD
  40. fi  # Проверка каталога перед очисткой лог-файлов.
  41. # более эффективный вариант:
  42. #
  43. # cd /var/log || {
  44. #   echo "Невозможно перейти в требуемый каталог." >&2
  45. #   exit $E_XCD;
  46. # }
  47. tail -$lines messages > mesg.temp # Сохранить последние строки в лог-файле.
  48. mv mesg.temp messages
  49. # cat /dev/null > messages
  50. #* Необходимость этой команды отпала, поскольку очистка выполняется выше.
  51. cat /dev/null > wtmp  #  команды ': > wtmp' и '> wtmp'  имеют тот же эффект.
  52. echo "Лог-файлы очищены."
  53. exit 0
  54. #  Возвращаемое значение 0
  55. #+ указывает на успешное завершение работы сценария.

Если вы не желаете полностью вычищать системные логи, то выше представлена улучшенная версия предыдущего сценария. Здесь сохраняются последние несколько строк (по-умолчанию — 50).

Если файл сценария начинается с последовательности #!, которая в мире Unix называется sha-bang, то это указывает системе какой интерпретатор следует использовать для исполнения сценария. Это двухбайтовая последовательность, или [4] — специальный маркер, определяющий тип сценария, в данном случае — сценарий командной оболочки (см. man magic). Более точно, sha-bang определяет интерпретатор, который вызывается для исполнения сценария, это может быть командная оболочка (shell), иной интерпретатор или утилита. [5]

  1. #!/bin/sh
  2. #!/bin/bash
  3. #!/usr/bin/perl
  4. #!/usr/bin/tcl
  5. #!/bin/sed -f
  6. #!/usr/awk -f


Каждая, из приведенных выше сигнатур, приводит к вызову различных интерпретаторов, будь то /bin/sh — командный интерпретатор по-умолчанию (bash для Linux-систем), либо иной. [6] При переносе сценариев с сигнатурой #!/bin/sh на другие Unix системы, где в качестве командного интерпретатора задан другой shell, вы можете лишиться некоторых особенностей, присущих bash. Поэтому такие сценарии должны быть POSIX совместимыми. [7].

Обратите внимание на то, что сигнатура должна указывать правильный путь к интерпретатору, в противном случае вы получите сообщение об ошибке — как правило это "Command not found".

Сигнатура #! может быть опущена, если вы не используете специфичных команд. Во втором примере (см. выше) использование сигнатуры #! обязательно, поскольку сценарий использует специфичную конструкцию присваивания значения переменной lines=50. Еще раз замечу, что сигнатура #!/bin/sh вызывает командный интерпретатор по-умолчанию — /bin/bash в Linux-системах.

Important

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

  1. if [ $# -ne Number_of_expected_args ]
  2. then
  3.   echo "Usage: `basename $0` whatever"
  4.   exit $WRONG_ARGS
  5. fi



[4]    Некоторые разновидности Unix (основанные на 4.2BSD) требуют, чтобы эта последовательность состояла из 4-х байт, за счет добавления пробела после !#! /bin/sh.

[5]    В shell-скриптах последовательность #! должна стоять самой первой и задает интерпретатор (sh или bash). Интерпретатор, в свою очередь, воспринимает эту строку как комментарий, поскольку она начинается с символа #.

[6]    Эта особенность позволяет использовать различные хитрости.

[7]    Portable Operating System Interface, попытка стандартизации UNIX-подобных операционных систем.