Advanced Bash-Scripting Guide: Искусство программирования на языке сценариев командной оболочки | ||
---|---|---|
Назад | Глава 12. Внешние команды, программы и утилиты | Вперед |
Команды, которые нельзя отнести ни к одной из вышеперечисленных категорий
Эти утилиты выводят последовательность целых чисел с шагом, заданным пользователем.
По-умолчанию, выводимые числа отделяются друг от друга символом перевода строки, однако, с помощью ключа -s может быть задан другой разделитель.
bash$ seq 5 1 2 3 4 5 bash$ seq -s : 5 1:2:3:4:5
Обе утилиты, и jot, и seq, очень удобно использовать для генерации списка аргументов в цикле for.
Пример 12-39. Использование seq для генерации списка аргументов цикла for
#!/bin/bash # Утилита "seq" echo for a in `seq 80` # или так: for a in $( seq 80 ) # То же самое, что и for a in 1 2 3 4 5 ... 80 (но как экономит время и силы!). # Можно использовать и 'jot' (если эта утилита имеется в системе). do echo -n "$a " done # 1 2 3 4 5 ... 80 # Пример использования вывода команды для генерации # [списка] аргументов цикла "for". echo; echo COUNT=80 # Да, 'seq' допускает указание переменных в качестве параметра. for a in `seq $COUNT` # или так: for a in $( seq $COUNT ) do echo -n "$a " done # 1 2 3 4 5 ... 80 echo; echo BEGIN=75 END=80 for a in `seq $BEGIN $END` # Если "seq" передаются два аргумента, то первый означает начальное число последовательности, #+ второй -- последнее, do echo -n "$a " done # 75 76 77 78 79 80 echo; echo BEGIN=45 INTERVAL=5 END=80 for a in `seq $BEGIN $INTERVAL $END` # Если "seq" передется три аргумента, то первый аргумент -- начальное число в последовательности, #+ второй -- шаг последовательности, #+ и третий -- последнее число в последовательности. do echo -n "$a " done # 45 50 55 60 65 70 75 80 echo; echo exit 0
Команда getopt служит для разбора командной строки, выделяя из нее ключи -- символы, с предшествующим знаком дефис. Этой утилите имеется, встроенный в Bash, аналог -- getopts, более мощная и универсальная команда.
Пример 12-40. Использование getopt для разбора аргументов командной строки
#!/bin/bash # ex33a.sh # Попробуйте следующие варианты вызова этого сценария. # sh ex33a -a # sh ex33a -abc # sh ex33a -a -b -c # sh ex33a -d # sh ex33a -dXYZ # sh ex33a -d XYZ # sh ex33a -abcd # sh ex33a -abcdZ # sh ex33a -z # sh ex33a a # Объясните полученные результаты. E_OPTERR=65 if [ "$#" -eq 0 ] then # Необходим по меньшей мере один аргумент. echo "Порядок использования: $0 -[options a,b,c]" exit $E_OPTERR fi set -- `getopt "abcd:" "$@"` # Запись аргументов командной строки в позиционные параметры. # Что произойдет, если вместо "$@" указать "$*"? while [ ! -z "$1" ] do case "$1" in -a) echo "Опция \"a\"";; -b) echo "Опция \"b\"";; -c) echo "Опция \"c\"";; -d) echo "Опция \"d\" $2";; *) break;; esac shift done # Вместо 'getopt' лучше использовать встроенную команду 'getopts', # См. "ex33.sh". exit 0
Команда run-parts [1] запускает на исполнение все сценарии, в порядке возрастания имен файлов-сценариев, в заданном каталоге. Естественно, файлы сценариев должны иметь права на исполнение.
Демон crond вызывает run-parts для запуска сценариев из каталогов /etc/cron.*.
По-умолчанию, команда yes выводит на stdout непрерывную последовательность символов y, разделенных символами перевода строки. Исполнение команды можно прервать комбинацией клавиш control-c. Команду yes можно заставить выводить иную последовательность символов. Теперь самое время задаться вопросом о практической пользе этой команды. Основное применение этой команды состоит в том, что вывод от нее может быть передан, через конвейер, другой команде, ожидающей реакции пользователя. В результате получается, своего рода, слабенькая версия команды expect.
yes | fsck /dev/hda1 запускает fsck в неинтерактивном режиме (будьте осторожны!).
yes | rm -r dirname имеет тот же эффект, что и rm -rf dirname (будьте осторожны!).
Печатает на stdout заданную строку символов (не более 10), рисуя каждый символ строки при помощи символа '#'. Вывод от команды может быть перенаправлен на принтер.
Выводит все переменные окружения текущего пользователя.
bash$ printenv | grep HOME HOME=/home/bozo
Команды lp и lpr отправляют файлы в очередь печати [2] для вывода на принтер. Названия этих команд произошли от "line printers".
bash$ lp file1.txt или bash lp <file1.txt
Очень часто используются в комбинации с командой форматированного вывода pr.
bash$ pr -options file1.txt | lp
Программы подготовки текста к печати, такие как groff и Ghostscript, так же могут напрямую взаимодействовать с lp.
bash$ groff -Tascii file.tr | lp
bash$ gs -options | lp file.ps
Команда lpq предназначена для просмотра очереди заданий печати, а lprm -- для удаления заданий из очереди.
[UNIX заимствовал эту идею из водопроводного дела.]
Это опрератор перенаправления, но с некоторыми особенностями. Подобно водопроводным трубам, "tee" позволяет "направить поток" данных в несколько файлов и на stdout одновременно, никак не влияя на сами данные. Эта команда может оказаться очень полезной при отладке.
tee |------> в файл | ===============|=============== command--->----|-operator-->---> результат работы команд(ы) ===============================cat listfile* | sort | tee check.file | uniq > result.file (Здесь, в файл check.file будут записаны данные из всех "listfile*", в отсортированном виде до того, как повторяющиеся строки будут удалены командой uniq.)
Эта, редко встречающаяся, команда создает именованный канал - очередь, через который производится обмен данными между процессами. [3] Как правило, один процесс записывает данные в очередь (FIFO), а другой читает данные из очереди. См. Пример A-17.
Производит проверку полного имени файла -- проверяет, доступны ли на чтение, каталоги в пути к файлу, и не превышает ли длина полного имени файла 255 символов. При несоблюдении одного из условий -- возвращает сообщение об ошибке.
К сожалению, pathchk не возвращает соответствующего кода ошибки, и потому, в общем-то, бесполезна в сценариях. Вместо нее лучше использовать операторы проверки файлов.
Эта немного непонятная и "страшная" команда ("data duplicator") изначально использовалась для переноса данных на магнитной ленте между микрокомпьютерами с ОС UNIX и майнфреймами IBM. Команда dd просто создает копию файла (или stdin/stdout), выполняя по пути некоторые преобразования. Один из вариантов: преобразование из ASCII в EBCDIC, [4] dd --help выведет список возможных вариантов преобразований и опций этой мощной утилиты.
# Изучаем 'dd'. n=3 p=5 input_file=project.txt output_file=log.txt dd if=$input_file of=$output_file bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null # Извлечет из $input_file символы с n-го по p-й. echo -n "hello world" | dd cbs=1 conv=unblock 2> /dev/null # Выведет "hello world" вертикально. # Спасибо, S.C.
Для демонстрации возможностей dd, попробуем перехватить нажатия на клавиши.
Пример 12-41. Захват нажатых клавиш
#!/bin/bash # Захват нажатых клавиш. keypresses=4 # Количество фиксируемых нажатий. old_tty_setting=$(stty -g) # Сохранить настройки терминала. echo "Нажмите $keypresses клавиши." stty -icanon -echo # Запретить канонический режим. # Запретить эхо-вывод. keys=$(dd bs=1 count=$keypresses 2> /dev/null) # 'dd' использует stdin, если "if" не задан. stty "$old_tty_setting" # Восстановить настройки терминала. echo "Вы нажали клавиши \"$keys\"." # Спасибо S.C. exit 0Команда dd имеет возможность произвольного доступа к данным в потоке.
echo -n . | dd bs=1 seek=4 of=file conv=notrunc # Здесь, опция "conv=notrunc" означает, что выходной файлне будет усечен. # Спасибо, S.C.
Команда dd может использоваться для создания образов дисков, считывая данные прямо с устройств, таких как дискеты, компакт диски, магнитные ленты (Пример A-6). Обычно она используется для создания загрузочных дискет.
dd if=kernel-image of=/dev/fd0H1440
Точно так же, dd может скопировать все содержимое дискеты, даже с неизвестной файловой системой, на жесткий диск в виде файла-образа.
dd if=/dev/fd0 of=/home/bozo/projects/floppy.img
Еще одно применение dd -- создание временного swap-файла (Пример 28-2) и ram-дисков (Пример 28-3). Она может создавать даже образы целых разделов жесткого диска, хотя и не рекомендуется делать это без особой на то необходимости.
Многие (которые, вероятно, не знают чем себя занять) постоянно придумывают все новые и новые области применения команды dd.
Пример 12-42. Надежное удаление файла
#!/bin/bash # blotout.sh: Надежно удаляет файл. # Этот суенарий записывает случайные данные в заданный файл, #+ затем записывает туда нули и наконец удаляет файл. # После такого удаления даже анализ дисковых секторов #+ не даст ровным счетом ничего. PASSES=7 # Количество проходов по файлу. BLOCKSIZE=1 # операции ввода/вывода в/из /dev/urandom требуют указания размера блока, #+ иначе вы не получите желаемого результата. E_BADARGS=70 E_NOT_FOUND=71 E_CHANGED_MIND=72 if [ -z "$1" ] # Имя файла не указано. then echo "Порядок использования: `basename $0` filename" exit $E_BADARGS fi file=$1 if [ ! -e "$file" ] then echo "Файл \"$file\" не найден." exit $E_NOT_FOUND fi echo; echo -n "Вы совершенно уверены в том, что желаете уничтожить \"$file\" (y/n)? " read answer case "$answer" in [nN]) echo "Передумали? Операция отменена." exit $E_CHANGED_MIND ;; *) echo "Уничтожается файл \"$file\".";; esac flength=$(ls -l "$file" | awk '{print $5}') # Поле с номером 5 -- это длина файла. pass_count=1 echo while [ "$pass_count" -le "$PASSES" ] do echo "Проход #$pass_count" sync # Вытолкнуть буферы. dd if=/dev/urandom of=$file bs=$BLOCKSIZE count=$flength # Заполнить файл случайными данными. sync # Снова вытолкнуть буферы. dd if=/dev/zero of=$file bs=$BLOCKSIZE count=$flength # Заполнить файл нулями. sync # Снова вытолкнуть буферы. let "pass_count += 1" echo done rm -f $file # Наконец удалить изрядно "подпорченный" файл. sync # Вытолкнуть буферы в последний раз. echo "Файл \"$file\" уничтожен."; echo # Это довольно надежный, хотя и достаточно медленный способ уничтожения файлов. #+ Более эффективно это делает команда "shred", #+ входящая в состав пакета GNU "fileutils". # Уничтоженный таким образом файл, не сможет быть восстановлен обычными методами. # Однако... #+ эта метода вероятно НЕ сможет противостоять аналитическим службам #+ из СООТВЕТСТВУЮЩИХ ОРГАНОВ # Tom Vier разработал пакет "wipe", который более надежно стирает файлы #+ чем этот простой сценарий. # http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2 # Для более глубоко изучения проблемы надежного удаления файлов, #+ рекомендую обратиться к cnfnmt Peter Gutmann, #+ "Secure Deletion of Data From Magnetic and Solid-State Memory". # http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html exit 0
Команда od (octal dump) производит преобразование ввода (или файла) в один или несколько форматов, в соответствии с указанными опциями. При отсутствии опций используется восьмеричный формат (опция -o). Эта команда полезна при просмотре или обработке файлов с двоичными данными, например /dev/urandom. См. Пример 9-26 и Пример 12-10.
Выводит дамп двоичных данных из файла в восьмеричном, шестнадцатиричном, десятичном виде или в виде ASCII. Эту команду, с массой оговорок, можно назвать эквивалентом команды of od.
Отображает содержимое исполняемого или объектного файла либо в шестнадцатиричной форме, либо в виде дизассемблерного листинга (с ключом -d).
bash$ objdump -d /bin/ls /bin/ls: file format elf32-i386 Disassembly of section .init: 080490bc <.init>: 80490bc: 55 push %ebp 80490bd: 89 e5 mov %esp,%ebp . . .
Эта команда создает псевдослучайные шестнадцатиричные 128-битные числа, так называемые "magic cookie", обычно используется X-сервером в качестве "сигнатуры" авторизации. В сценариях может использоваться как малоэффективный генератор случайных чисел.
random000=`mcookie | sed -e '2p'` # 'sed' удаляет посторонние символы.
Конечно, для тех же целей, сценарий может использовать md5.
# Сценарий вычисляет контрольную сумму для самого себя. random001=`md5sum $0 | awk '{print $1}'` # 'awk' удаляет имя файла.
С помощью mcookie можно создавать "уникальные" имена файлов.
Пример 12-43. Генератор имен файлов
#!/bin/bash # tempfile-name.sh: Генератор имен временных файлов BASE_STR=`mcookie` # 32-символьный (128 бит) magic cookie. POS=11 # Произвольная позиция в строке magic cookie. LEN=5 # $LEN последовательных символов. prefix=temp # В конце концов это временный ("temp") файл. suffix=${BASE_STR:POS:LEN} # Извлечь строку, длиной в 5 символов, начиная с позиции 11. temp_filename=$prefix.$suffix # Сборка имени файла. echo "Имя временного файла = \"$temp_filename\"" # sh tempfile-name.sh # Имя временного файла = temp.e19ea exit 0
Эта утилита производит преобразование величин из одних единиц измерения в другие. Как правило вызывается в интерактивном режиме, ниже приводится пример использования units в сценарии.
Пример 12-44. Преобразование метров в мили
#!/bin/bash # unit-conversion.sh convert_units () # Принимает в качестве входных параметров единицы измерения. { cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}') # Удаляет все кроме коэффициентов преобразования. echo "$cf" } Unit1=miles Unit2=meters cfactor=`convert_units $Unit1 $Unit2` quantity=3.73 result=$(echo $quantity*$cfactor | bc) echo "В $quantity милях $result метров." # Что произойдет, если в функцию передать несовместимые единицы измерения, #+ например "acres" (акры) and "miles" (мили)? exit 0
Не команда, а клад, m4 -- это мощный фильтр обработки макроопределений, [5] фактически -- целый язык программирования. Изначально создававшаяся как препроцессор для RatFor, m4 оказалась очень полезной и как самостоятельная утилита. Фактически, m4 сочетает в себе функциональные возможности eval, tr, awk, и дополнительно предоставляет обширные возможности по созданию новых макроопределений.
В апрельском выпуске, за 2002 год, журнала Linux Journal вы найдете замечательную статью, описывающую возможности утилиты m4.
Пример 12-45. Пример работы с m4
#!/bin/bash # m4.sh: Демонстрация некоторых возможносией макропроцессора m4 # Строки string=abcdA01 echo "len($string)" | m4 # 7 echo "substr($string,4)" | m4 # A01 echo "regexp($string,[0-1][0-1],\&Z)" | m4 # 01Z # Арифметика echo "incr(22)" | m4 # 23 echo "eval(99 / 3)" | m4 # 33 exit 0
Команда doexec предоставляет возможность передачи произвольного списка аргументов внешней программе. В частности, передавая argv[0] (для сценариев соответствует специальной переменной $0), можно вызвать программу под другим именем, определяя тем самым, ее реакцию.
Например, Пусть в каталоге /usr/local/bin имеется программа с именем "aaa", которая при вызове doexec /usr/local/bin/aaa list выведет список всех файлов в текущем каталоге, имена которых начинаются с символа "a", а при вызове той же самой программы как doexec /usr/local/bin/aaa delete , она удалит эти файлы.
Естественно, реакция программы на свое собственное имя должна быть реализована в коде программы, для сценария на языке командной оболочки это может выглядеть примерно так: case `basename $0` in "name1" ) реакция на вызов под именем name1;; "name2" ) реакция на вызов под именем name2;; "name3" ) реакция на вызов под именем name3;; * ) действия по-умолчанию;; esac |
[1] |
Фактически -- это сценарий, заимствованный из дистрибутива Debian Linux. |
[2] |
Очередь печати -- это группа заданий "ожидающих вывода" на принтер. |
[3] |
Эта тема прекрасно освещена в статье, которую написал Andy Vaught, Introduction to Named Pipes, в сентябре 1997 для Linux Journal. |
[4] |
EBCDIC (произносится как "ebb-sid-ic") -- это аббревиатура от Extended Binary Coded Decimal Interchange Code (Расширенный Двоично-Десятичный Код Обмена Информацией). Это формат представления данных от IBM, не нашедший широкого применения. Не совсем обычное применение опции conv=ebcdic -- это использовать dd для быстрого и легкого, но слабого, шифрования текстовых файлов. cat $file | dd conv=swab,ebcdic > $file_encrypted # Зашифрованный файл будет выглядеть как "абракадабра". # опция swab добавлена для внесения большей неразберихи. cat $file_encrypted | dd conv=swab,ascii > $file_plaintext # Декодирование. |
[5] |
макроопределение -- это идентификатор, символическая константа, которая представляет некоторую последовательность команд, операций и параметров. |
Назад | К началу | Вперед |
Команды выполнения математических операций | Наверх | Команды системного администрирования |