Перейти к основному содержанию
Рецепты Linux

Main navigation

  • Основы
  • Система
  • Команды
  • Программы
  • Дистро
  • Интерфейсы
  • Устройства
  • Доки
User account menu
  • Войти

Строка навигации

  1. Главная
  2. ABS Guide
  3. Часть 4. Материал повышенной сложности
  4. Глава 33. Разное

33.7. Разные советы

  • Для ведения учета использования сценария пользователями, добавьте следующие строки в сценарий. Они запишут в файл отчета название сценария и время запуска.

    # Добавление (>>) учетной записи, об использовании сценария, в файл отчета.
    
    date>> $SAVE_FILE # Дата и время.
    echo $0>> $SAVE_FILE # Название сценария.
    echo>> $SAVE_FILE # Пустая строка -- как разделитель записей.
    
    # Не забудьте определить переменную окружения SAVE_FILE в ~/.bashrc
    # (что нибудь, типа: ~/.scripts-run)
    
  • Оператор >> производит добавление строки в конец файла. А как быть, если надо добавить строку в начало существующего файла?

    file=data.txt
    title="***Это титульная строка в текстовом файле***"
    
    echo $title | cat - $file >$file.new
    # "cat -" объединяет stdout с содержимым $file.
    # В результате получится
    #+ новый файл $file.new, в начало которого добавлена строка $title.
    

    Само собой разумеется, то же самое можно сделать с помощью sed.

  • Сценарий командной оболочки может использоваться как команда внутри другого сценария командной оболочки, Tcl, или wish сценария или, даже в Makefile. Он может быть вызван как внешняя команда из программы на языке C, с помощью функции system(), т.е. system("script_name");.

  • Собирайте свои библиотеки часто используемых функций и определений. Эти "библиотеки" могут быть "подключены" к сценариям, с помощью команды точка (.) или source.

    # Сценарий-библиотека
    # ------ -------
    
    # Обратите внимание:
    # Здесь нет sha-bang ("#!").
    # И нет "живого кода".
    
    
    # Определения переменных
    
    ROOT_UID=0 # UID root-а, 0.
    E_NOTROOT=101 # Ошибка -- "обычный пользователь".
    MAXRETVAL=255 # Максимальное значение, которое могут возвращать функции.
    SUCCESS=0
    FAILURE=-1
    
    
    
    # Функции
    
    Usage () # Сообщение "Порядок использования:".
    {
     if [ -z "$1" ] # Нет аргументов.
     then
     msg=filename
     else
     msg=$@
     fi
    
     echo "Порядок использования: `basename $0` "$msg""
    }
    
    
    Check_if_root () # Проверка прав пользователя.
    { # из примера "ex39.sh".
     if [ "$UID" -ne "$ROOT_UID" ]
     then
     echo "Этот сценарий должен запускаться с привилегиями root."
     exit $E_NOTROOT
     fi
    }
    
    
    CreateTempfileName () # Создание "уникального" имени для временного файла.
    { # Из примера "ex51.sh".
     prefix=temp
     suffix=`eval date +%s`
     Tempfilename=$prefix.$suffix
    }
    
    
    isalpha2 () # Проверка, состоит ли строка только из алфавитных символов.
    { # Из примера "isalpha.sh".
     [ $# -eq 1 ] || return $FAILURE
    
     case $1 in
     *[!a-zA-Z]*|"") return $FAILURE;;
     *) return $SUCCESS;;
     esac # Спасибо S.C.
    }
    	
    	
    abs () # Абсолютное значение.
    { # Внимание: Максимально возможное возвращаеиое значение
     # не может превышать 255.
     E_ARGERR=-999999
    					 
     if [ -z "$1" ] # Проверка наличия входного аргумента.
     then
     return $E_ARGERR # Код ошибки, обычно возвращаемый в таких случаях.
     fi
    						 
     if [ "$1" -ge 0 ] # Если не отрицательное,
     then #
     absval=$1 # оставить как есть.
     else # Иначе,
     let "absval = (( 0 - $1 ))" # изменить знак.
     fi
     return $absval
    }
    								 
    								 
    tolower () # Преобразование строк символов в нижний регистр
    {
    								 
     if [ -z "$1" ] # Если нет входного аргумента,
     then #+ выдать сообщение об ошибке
     echo "(null)"
     return #+ и выйти из функции.
     fi
    										 
     echo "$@" | tr A-Z a-z
     # Преобразовать все входные аргументы ($@).
    											 
     return
    											 
    # Для записи результата работы функции в переменную, используйте операцию подстановки команды.
    # Например:
    # oldvar="A seT of miXed-caSe LEtTerS"
    # newvar=`tolower "$oldvar"`
    # echo "$newvar" # a set of mixed-case letters
    #
    # Упражнение: Добавьте в эту библиотеку функцию перевода символов в верхний регистр.
    # toupper() [это довольно просто].
    }
    
  • Для повышения ясности комментариев, выделяйте их особым образом.

    ## Внимание!
    rm -rf *.zzy ## Комбинация ключей "-rf", в команде "rm", чрезвычайно опасна,
     ##+ особенно при удалении по шаблону.
    
    #+ Продолжение комментария на новой строке.
    # Это первая строка комментария
    #+ это вторая строка комментария,
    #+ это последняя строка комментария.
    
    #* Обратите внимание.
    
    #o Элемент списка.
    
    #> Альтернативный вариант.
    while [ "$var1" != "end" ] #> while test "$var1" != "end"
    
  • Для создания блочных комментариев, можно использовать конструкцию if-test.

    #!/bin/bash
    
    COMMENT_BLOCK=
    # Если попробовать инициализировать эту переменную чем нибудь,
    #+ то вы получите неожиданный результат.
    
    if [ $COMMENT_BLOCK ]; then
    
    Блок комментария --
    =================================
    Это строка комментария.
    Это другая строка комментария.
    Это еще одна строка комментария.
    =================================
    
    echo "Эта строка не выводится."
    
    Этот блок комментария не вызывает сообщения об ошибке! Круто!
    
    fi
    
    echo "Эта строка будет выведена на stdout."
    
    exit 0
    

    Сравните этот вариант создания блочных комментариев со встроенным документом, использующимся для создания блочных комментариев.

  • С помощью служебной переменной $?, можно проверить -- является ли входной аргумент целым числом.

    #!/bin/bash
    
    SUCCESS=0
    E_BADINPUT=65
    
    test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null
    # Проверка: "равно нулю или не равно нулю".
    # 2>/dev/null подавление вывода сообщений об ошибках.
    
    if [ $? -ne "$SUCCESS" ]
    then
     echo "Порядок использования: `basename $0` целое_число"
     exit $E_BADINPUT
    fi
    
    let "sum = $1 + 25" # Будет выдавать ошибку, если $1 не является целым числом.
    echo "Sum = $sum"
    
    # Любая переменная может быть проверена таким образом, а не только входные аргументы.
    
    exit 0
    
  • Диапазон, возвращаемых функциями значений, 0 - 255 -- серьезное ограничение. Иногда может оказаться весьма проблематичным использование глобальных переменных, для передачи результата из функции. В таких случаях можно порекомендовать передачу результатов работы функции через запись в stdout. Фактически этот прием является разновидностью подстановки команд.

    Пример 33-11. Необычный способ передачи возвращаемого значения

    #!/bin/bash
    # multiplication.sh
    
    multiply () # Функции выполняет перемножение всех переданых аргументов.
    {
     local product=1
    
     until [ -z "$1" ] # Пока не дошли до последнего аргумента...
     do
     let "product *= $1"
     shift
     done
    
     echo $product # Значение не будет выведено на экран,
    } #+ поскольку оно будет записано в переменную.
    
    mult1=15383; mult2=25211
    val1=`multiply $mult1 $mult2`
    echo "$mult1 X $mult2 = $val1"
     # 387820813
    
    mult1=25; mult2=5; mult3=20
    val2=`multiply $mult1 $mult2 $mult3`
    echo "$mult1 X $mult2 X $mult3 = $val2"
     # 2500
    
    mult1=188; mult2=37; mult3=25; mult4=47
    val3=`multiply $mult1 $mult2 $mult3 $mult4`
    echo "$mult1 X $mult2 X $mult3 X mult4 = $val3"
     # 8173300
    
    exit 0
    

    Такой прием срабатывает и для строковых значений. Таким образом, функция может "возвращать" и нечисловой результат.

    capitalize_ichar () # Первый символ всех строковых аргументов
    { #+ переводится в верхний регистр.
    
     string0="$@" # Принять все аргументы.
    
     firstchar=${string0:0:1} # Первый символ.
     string1=${string0:1} # Остаток строки.
    
     FirstChar=`echo "$firstchar" | tr a-z A-Z`
     # Преобразовать в верхний регистр.
    
     echo "$FirstChar$string1" # Выдать на stdout.
    
    }
    
    newstring=`capitalize_ichar "each sentence should start with a capital letter."`
    echo "$newstring" # Each sentence should start with a capital letter.
    

    Используя этот прием, функция может "возвращать" даже несколько значений.

    Пример 33-12. Необычный способ получения нескольких возвращаемых значений

    #!/bin/bash
    # sum-product.sh
    # Функция может "возвращать" несколько значений.
    
    sum_and_product () # Вычисляет сумму и произведение аргументов.
    {
     echo $(( $1 + $2 )) $(( $1 * $2 ))
    # Вывод на stdout двух значений, разделенных пробелом.
    }
    
    echo
    echo "Первое число: "
    read first
    
    echo
    echo "Второе число: "
    read second
    echo
    
    retval=`sum_and_product $first $second` # Получить результат.
    sum=`echo "$retval" | awk '{print $1}'` # Первое значение (поле).
    product=`echo "$retval" | awk '{print $2}'` # Второе значение (поле).
    
    echo "$first + $second = $sum"
    echo "$first * $second = $product"
    echo
    
    exit 0
    
  • Следующая хитрость -- передача массива в функцию, и "возврат" массива из функции.

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

    Пример 33-13. Передача массива в функцию и возврат массива из функции

    #!/bin/bash
    # array-function.sh: Передача массива в функцию и...
    # "возврат" массива из функции
    
    
    Pass_Array ()
    {
     local passed_array # Локальная переменная.
     passed_array=( `echo "$1"` )
     echo "${passed_array[@]}"
     # Список всех элементов в новом массиве,
     #+ объявленном и инициализированном в функции.
    }
    
    
    original_array=( element1 element2 element3 element4 element5 )
    
    echo
    echo "original_array = ${original_array[@]}"
    # Список всех элементов исходного массива.
    
    
    # Так можно отдать массив в функцию.
    # **********************************
    argument=`echo ${original_array[@]}`
    # **********************************
    # Поместив все элементы массива в переменную,
    #+ разделяя их пробелами.
    #
    # Обратите внимание: метод прямой передачи массива в функцию не сработает.
    
    
    # Так можно получить массив из функции.
    # *****************************************
    returned_array=( `Pass_Array "$argument"` )
    # *****************************************
    # Записать результат в переменную-массив.
    
    echo "returned_array = ${returned_array[@]}"
    
    echo "============================================================="
    
    # А теперь попробуйте получить доступ к локальному массиву
    #+ за пределами функции.
    Pass_Array "$argument"
    
    # Функция выведет массив, но...
    #+ доступ к локальному массиву, за пределами функции, окажется невозможен.
    echo "Результирующий массив (внутри функции) = ${passed_array[@]}"
    # "ПУСТОЕ" ЗНАЧЕНИЕ, поскольку это локальная переменная.
    
    echo
    
    exit 0
    

    Более сложный пример передачи массивов в функции, вы найдете в Пример A-11.

  • Использование конструкций с двойными круглыми скобками позволяет применять C-подобный синтаксис операций присвоения и инкремента переменных, а также оформления циклов for и while. См. Пример 10-12 и Пример 10-17.

  • Иногда очень удобно "пропускать" данные через один и тот же фильтр, но с разными параметрами, используя конвейерную обработку. Особенно это относится к tr и grep.

    # Из примера "wstrings.sh".
    
    wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
    tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`
    

    Пример 33-14. Игры с анаграммами

    #!/bin/bash
    # agram.sh: Игры с анаграммами.
    
    # Поиск анаграмм...
    LETTERSET=etaoinshrdlu
    
    anagram "$LETTERSET" | # Найти все анаграммы в наборе символов...
    grep '.......' | # состоящие, как минимум из 7 символов,
    grep '^is' | # начинающиеся с 'is'
    grep -v 's$' | # исключая множественное число
    grep -v 'ed$' # и глаголы в прошедшем времени
    
    # Здесь используется утилита "anagram"
    #+ которая входит в состав пакета "yawl" , разработанного автором.
    # http://ibiblio.org/pub/Linux/libs/yawl-0.2.tar.gz
    
    exit 0 # Конец.
    
    bash$ sh agram.sh
    islander
    isolate
    isolead
    isotheral
    

    См. также Пример 27-3, Пример 12-21 и Пример A-10.

  • Для создания блочных комментариев можно использовать "анонимные встроенные документы". См. Пример 17-11.

  • Попытка вызова утилиты из сценария на машине, где эта утилита отсутствует, потенциально опасна. Для обхода подобных проблем можно воспользоваться утилитой whatis.

    CMD=command1 # Основной вариант.
    PlanB=command2 # Запасной вариант.
    
    command_test=$(whatis "$CMD" | grep 'nothing appropriate')
    # Если 'command1' не найдена в системе, то 'whatis' вернет
    #+ "command1: nothing appropriate."
    #==> От переводчика: Будьте внимательны! Если у вас локализованная версия whatis
    #==> то вывод от нее может отличаться от используемого здесь ('nothing appropriate')
    
    
    if [[ -z "$command_test" ]] # Проверка наличия утилиты в системе.
    then
     $CMD option1 option2 # Запуск команды с параметрами.
    else # Иначе,
     $PlanB #+ запустить command2 (запасной вариант).
    fi
    
  • Команда run-parts удобна для запуска нескольких сценариев, особенно в комбинации с cron или at.

  • Иногда было бы неплохо снабдить сценарий графическим интерфейсом X-Window. Для этого можно порекомендовать пакеты Xscript, Xmenu и widtools. Правда, первые два, кажется больше не поддерживаются разработчиками.

    Caution

    Пакет widtools (widget tools) требует наличия библиотеки XForms. Кроме того, необходимо слегка подправить Makefile, чтобы этот пакет можно было собрать на типичной Linux-системе. Но хуже всего то, что три из шести виджетов не работают :-(( (segfault).

    Утилита dialog -- еще один способ создания диалоговых форм из сценариев командной оболочки. Эта утилита предназначена для работы в текстовой консоли, но имеются ее "наследники" -- gdialog, Xdialog и kdialog, которые используют графические элементы X-Windows для построения диалоговых форм.

    Пример 33-15. Сценарий с графическим интерфейсом

    #!/bin/bash
    # dialog.sh: Использование виджетов 'gdialog'.
    # В вашей системе должна быть установлена утилита 'gdialog'.
    
    # Идея создания этого сценария появилась после прочтения статьи
    # "Scripting for X Productivity," by Marco Fioretti,
    # LINUX JOURNAL, Issue 113, September 2003, pp. 86-9.
    # Спасибо всем сотрудникам LJ.
    
    
    # Ошибка ввода в диалоговом окне.
    E_INPUT=65
    # Размеры окна.
    HEIGHT=50
    WIDTH=60
    
    # Имя выходного файла (конструируется добавлением суффикса к имени файла-сценария).
    OUTFILE=$0.output
    
    # Вывести содержимое файла-сценария в отдельном окне.
    gdialog --title "Displaying: $0" --textbox $0 $HEIGHT $WIDTH
    
    
    # Попробуем записать значение, введенное в окне.
    echo -n "VARIABLE=\"" > $OUTFILE # Кавычка на случай, если пользователь введет
     #+ несколько слов, разделенных пробелами.
    gdialog --title "User Input" --inputbox "Введите значение переменной:" \
    $HEIGHT $WIDTH >> $OUTFILE
    				 
    				 
    if [ "$?" -eq 0 ]
    # Хороший тон -- проверка кода завершения.
    then
     echo "Диалог с пользователем завершился без ошибок."
    else
     echo "Обнаружены ошибки во время диалога с пользователем."
     # Или была нажата кнопка "Отменить" ("Cancel") вместо "OK".
     rm $OUTFILE
     exit $E_INPUT
    fi
    						 
    						 
    echo -n "\"" >> $OUTFILE # Завершающая кавычка (см. выше).
    						 
    						 
    # Теперь прочитаем значение переменной из файла и выведем его.
    . $OUTFILE # 'Выходной' файл.
    echo "Было введено значение переменной: "$VARIABLE""
    						 
    rm $OUTFILE # Удалить временный файл.
    						 
    exit 0
    

    Кроме того, для построения приложений с графическим интерфейсом, можно попробовать Tk, или wish (надстройка над Tcl), PerlTk (Perl с поддержкой Tk), tksh (ksh с поддержкой Tk), XForms4Perl (Perl с поддержкой XForms), Gtk-Perl (Perl с поддержкой Gtk) или PyQt (Python с поддержкой Qt).

Перекрёстные ссылки книги для 33.7. Разные советы

  • 33.6. Оптимизация
  • Вверх
  • 33.8. Проблемы безопасности

Book navigation

  • Содержание
  • Часть 1. Введение
  • Часть 2. Основы
  • Часть 3. Углубленный материал
  • Часть 4. Материал повышенной сложности
    • Глава 18. Регулярные выражения
    • Глава 19. Подоболочки, или Subshells
    • Глава 20. Ограниченный режим командной оболочки
    • Глава 21. Подстановка процессов
    • Глава 22. Функции
    • Глава 23. Псевдонимы
    • Глава 24. Списки команд
    • Глава 25. Массивы
    • Глава 26. Файлы
    • Глава 27. /dev и /proc
    • Глава 28. /dev/zero и /dev/null
    • Глава 29. Отладка сценариев
    • Глава 30. Необязательные параметры (ключи)
    • Глава 31. Широко распространенные ошибки
    • Глава 32. Стиль программирования
    • Глава 33. Разное
      • 33.1. Интерактивный и неинтерактивный режим работы
      • 33.2. Сценарии-обертки
      • 33.3. Операции сравнения: Альтернативные решения
      • 33.4. Рекурсия
      • 33.5. "Цветные" сценарии
      • 33.6. Оптимизация
      • 33.7. Разные советы
      • 33.8. Проблемы безопасности
      • 33.9. Проблемы переносимости
      • 33.10. Сценарии командной оболочки под Windows
    • Глава 34. Bash, версия 2
  • Глава 35. Замечания и дополнения
  • Библиография
  • Приложение A. Дополнительные примеры сценариев
  • Приложение B. Справочная информация
  • Приложение C. Маленький учебник по Sed и Awk
  • Приложение D. Коды завершения, имеющие предопределенный смысл
  • Приложение E. Подробное введение в операции ввода-вывода и перенаправление ввода-вывода
  • Приложение F. Системные каталоги
  • Приложение G. Локализация
  • Приложение H. История команд
  • Приложение I. Пример файла .bashrc
  • Приложение J. Преобразование пакетных (*.bat) файлов DOS в сценарии командной оболочки
  • Приложение K. Упражнения
  • Приложение L. Хронология
  • Приложение M. Авторские права

Последние материалы

  • Приложение scanimage
    1 day ago
  • Утилита sensors
    5 days ago
  • Сканер Rkhunter
    1 week 5 days ago
  • Программа resize2fs
    2 weeks 3 days ago
  • Аудиопроигрыватель QMMP
    3 weeks 2 days ago
RSS feed

Secondary menu

  • О проекте

© 2008–2025 Олег Меньшенин mensh@yandex.ru