hypotenuse () # Расчет гипотенузы прямоугольного треугольника.

{

hyp=$(bc - l << EOF

scale = 9

sqrt ( $1 * $1 + $2 * $2 )

EOF

)

# К сожалению, функции Bash не могут возвращать числа с плавающей запятой.

}

hypotenuse 3

echo "гипотенуза = $hyp" # 8.

exit 0

Пример 12-35. Вычисление числа "пи"

#!/bin/bash

# cannon. sh: Аппроксимация числа "пи".

# Это очень простой вариант реализации метода "Monte Carlo",

#+ математическое моделирование событий реальной жизни,

#+ для эмуляции случайного события используются псевдослучайные числа.

# Допустим, что мы располагаем картой квадратного участка поверхности со стороной квадрата 10000 единиц.

# На этом участке, в центре, находится совершенно круглое озеро,

#+ с диаметром в 10000 единиц.

# Т. е. озеро покрывает почти всю карту, кроме ее углов.

# (Фактически -- это квадрат со вписанным кругом.)

#

# Пусть по этому участку ведется стрельба железными ядрами из древней пушки

# Все ядра падают где-то в пределах данного участка,

#+ т. е. либо в озеро, либо на сушу, по углам участка.

# Поскольку озеро покрывает большую часть участка,

#+ то большинство ядер будет падать в воду.

# Незначительная часть ядер будет падать на твердую почву.

#

# Если произвести достаточно большое число неприцельных выстрелов по данному участку,

#+ то отношение попаданий в воду к общему числу выстрелов будет примерно равно

#+ значению PI/4.

#

# По той простой причине, что стрельба фактически ведется только

#+ по правому верхнему квадранту карты.

# (Предыдущее описание было несколько упрощено.)

НЕ нашли? Не то? Что вы ищете?

#

# Теоретически, чем больше будет произведено выстрелов, тем точнее будет результат.

# Однако, сценарий на языке командной оболочки, в отличие от других языков программирования,

#+ в которых доступны операции с плавающей запятой, имеет некоторые ограничения.

# К сожалению, это делает вычисления менее точными.

DIMENSION=10000 # Длина стороны квадратного участка поверхности.

# Он же -- верхний предел для генератора случайных чисел.

MAXSHOTS=1000 # Количество выстрелов.

# 10000 выстрелов (или больше) даст лучший результат,

# но потребует значительного количества времени.

PMULTIPLIER=4.0 # Масштабирующий коэффициент.

get_random ()

{

SEED=$(head -1 /dev/urandom | od - N 1 | awk '{ print $2 }')

RANDOM=$SEED # Из примера "seeding-random. sh"

let "rnum = $RANDOM % $DIMENSION" # Число не более чем 10000.

echo $rnum

}

distance= # Объявление глобальной переменной.

hypotenuse () # Расчет гипотенузы прямоугольного треугольника.

{ # Из примера "alt-bc. sh".

distance=$(bc - l << EOF

scale = 0

sqrt ( $1 * $1 + $2 * $2 )

EOF

)

# Установка "scale" в ноль приводит к округлению результата "вниз",

#+ это и есть то самое ограничение, накладываемое командной оболочкой.

# Что, к сожалению, снижает точность аппроксимации.

}

# main() {

# Инициализация переменных.

shots=0

splashes=0

thuds=0

Pi=0

while [ "$shots" - lt "$MAXSHOTS" ] # Главный цикл.

do

xCoord=$(get_random) # Получить случайные координаты X и Y.

yCoord=$(get_random)

hypotenuse $xCoord $yCoord # Гипотенуза = расстоянию.

((shots++))

printf "#%4d " $shots

printf "Xc = %4d " $xCoord

printf "Yc = %4d " $yCoord

printf "Distance = %5d " $distance # Растояние от

#+ центра озера,

#+ с координатами (0,0).

if [ "$distance" - le "$DIMENSION" ]

then

echo - n "ШЛЕП! " # попадание в озеро

((splashes++))

else

echo - n "БУХ! " # попадание на твердую почву

((thuds++))

fi

Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc)

# Умножение на коэффициент 4.0.

echo - n "PI ~ $Pi"

echo

done

echo

echo "После $shots выстрела, примерное значение числа \"пи\" равно $Pi."

# Имеет тенденцию к завышению...

# Вероятно из-за ошибок округления и несовершенства генератора случайных чисел.

echo

# }

exit 0

# Самое время задуматься над тем, является ли сценарий удобным средством

#+ для выполнения большого количества столь сложных вычислений.

#

# Тем не менее, этот пример может расцениваться как

# 1) Доказательство возможностей языка командной оболочки.

# 2) Прототип для "обкатки" алгоритма перед тем как перенести

#+ его на высокоуровневые языки программирования компилирующего типа.

dc

Утилита dc (desk calculator) -- это калькулятор, использующий "Обратную Польскую Нотацию", и ориентированный на работу со стеком.

Многие стараются избегать испоьзования dc, из-за непривычной формы записи операндов и операций. Однако, dc имеет и своих сторонников.

Пример 12-36. Преобразование чисел из десятичной в шестнадцатиричную систему счисления

#!/bin/bash

# hexconvert. sh: Преобразование чисел из десятичной в шестнадцатиричную систему счисления.

BASE=16 # Шестнадцатиричная.

if [ - z "$1" ]

then

echo "Порядок использования: $0 number"

exit $E_NOARGS

# Необходим аргумент командной строки.

fi

# Упражнение: добавьте проверку корректности аргумента.

hexcvt ()

{

if [ - z "$1" ]

then

echo 0

return # "Return" 0, если функции не был передан аргумент.

fi

echo ""$1" "$BASE" o p" | dc

# "o" устанавливает основание системы счисления для вывода.

# "p" выводит число, находящееся на вершине стека.

# См. 'man dc'.

return

}

hexcvt "$1"

exit 0

Изучение страниц info dc позволит детальнее разобраться с утилитой. Однако, отряд "гуру", которые могут похвастать своим знанием этой мощной, но весьма запутанной утилиты, весьма немногочислен.

Пример 12-37. Разложение числа на простые множители

#!/bin/bash

# factr. sh: Разложение числа на простые множители

MIN=2 # Не работает с числами меньше 2.

E_NOARGS=65

E_TOOSMALL=66

if [ - z $1 ]

then

echo "Порядок использования: $0 number"

exit $E_NOARGS

fi

if [ "$1" - lt "$MIN" ]

then

echo "Исходное число должно быть больше или равно $MIN."

exit $E_TOOSMALL

fi

# Упражнение: Добавьте проверку типа числа (не целые числа должны отвергаться).

echo "Простые множители для числа $1:"

# ----

echo "$1[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds. xd1<2" | dc

# ----

# Автор вышеприведенной строки: Michel Charpentier <*****@***unh. edu>.

# Используется с его разрешения (спасибо).

exit 0

awk

Еще один способ выполнения математических операций, над числами с плавающей запятой, состоит в создании сценария-обертки, использующего математические функции awk.

Пример 12-38. Расчет гипотенузы прямоугольного треугольника

#!/bin/bash

# hypotenuse. sh: Возвращает "гипотенузу" прямоугольного треугольника.

# ( корень квадратный от суммы квадратов катетов)

ARGS=2 # В сценарий необходимо передать два катета.

E_BADARGS=65 # Ошибка в аргументах.

if [ $# - ne "$ARGS" ] # Проверка количества аргументов.

then

echo "Порядок использования: `basename $0` катет_1 катет_2"

exit $E_BADARGS

fi

AWKSCRIPT=' { printf( "%3.7f\n", sqrt($1*$1 + $2*$2) ) } '

# команды и параметры, передаваемые в awk

echo - n "Гипотенуза прямоугольного треугольника, с катетами $1 и $2, = "

echo $1 $2 | awk "$AWKSCRIPT"

exit 0

12.9. Прочие команды

Команды, которые нельзя отнести ни к одной из вышеперечисленных категорий

jot, seq

Эти утилиты выводят последовательность целых чисел с шагом, заданным пользователем.

По-умолчанию, выводимые числа отделяются друг от друга символом перевода строки, однако, с помощью ключа - 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 (но как экономит время и силы!).

# Можно использовать и 'jot' (если эта утилита имеется в системе).

do

echo - n "$a "

done #

# Пример использования вывода команды для генерации

# [списка] аргументов цикла "for".

echo; echo

COUNT=80 # Да, 'seq' допускает указание переменных в качестве параметра.

for a in `seq $COUNT` # или так: for a in $( seq $COUNT )

do

echo - n "$a "

done #

echo; echo

BEGIN=75

END=80

for a in `seq $BEGIN $END`

# Если "seq" передаются два аргумента, то первый означает начальное число последовательности,

#+ второй -- последнее,

do

echo - n "$a "

done #80

echo; echo

BEGIN=45

INTERVAL=5

END=80

for a in `seq $BEGIN $INTERVAL $END`

# Если "seq" передется три аргумента, то первый аргумент -- начальное число в последовательности,

#+ второй -- шаг последовательности,

#+ и третий -- последнее число в последовательности.

do

echo - n "$a "

done #70 75 80

echo; echo

exit 0

getopt

Команда getopt служит для разбора командной строки, выделяя из нее ключи -- символы, с предшествующим символом дефиса. Этой утилите имеется, встроенный в Bash, аналог -- getopts, более мощная и универсальная команда. Тем не менее, команда getopt, с ключом - l, позволяет производить разбор "длинных" ключей.

Пример 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

Команда run-parts [33] запускает на исполнение все сценарии, в порядке возрастания имен файлов-сценариев, в заданном каталоге. Естественно, файлы сценариев должны иметь права на исполнение.

Демон crond вызывает run-parts для запуска сценариев из каталогов /etc/cron.*.

yes

По-умолчанию, команда yes выводит на stdout непрерывную последовательность символов y, разделенных символами перевода строки. Исполнение команды можно прервать комбинацией клавиш control-c. Команду yes можно заставить выводить иную последовательность символов. Теперь самое время задаться вопросом о практической пользе этой команды. Основное применение этой команды состоит в том, что вывод от нее может быть передан, через конвейер, другой команде, ожидающей реакции пользователя. В результате получается, своего рода, слабенькая версия команды expect.

yes | fsck /dev/hda1 запускает fsck в неинтерактивном режиме (будьте осторожны!).

yes | rm - r dirname имеет тот же эффект, что и rm - rf dirname (будьте осторожны!).

Warning

Внимание! Передача вывода команды yes по конвейеру потенциально опасным командам, таким как fsck или fdisk может дать нежелательные побочные эффекты.

banner

Печатает на stdout заданную строку символов (не более 10), рисуя каждый символ строки при помощи символа '#'. Вывод от команды может быть перенаправлен на принтер.

printenv

Выводит все переменные окружения текущего пользователя.

bash$

printenv | grep HOME

HOME=/home/bozo

lp

Команды lp и lpr отправляют файлы в очередь печати [34] для вывода на принтер. Названия этих команд произошли от "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 -- для удаления заданий из очереди.

tee

[UNIX заимствовал эту идею из водопроводного дела.]

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

tee

|------> в файл

|

===============|===============

command--->----|-operator-->---> результат работы команд(ы)

===============================

cat listfile* | sort | tee check. file | uniq > result. file

(Здесь, в файл check. file будут записаны данные из всех "listfile*", в отсортированном виде до того, как повторяющиеся строки будут удалены командой uniq.)

mkfifo

Эта, редко встречающаяся, команда создает именованный канал - очередь, через который производится обмен данными между процессами. [35] Как правило, один процесс записывает данные в очередь (FIFO), а другой читает данные из очереди. См. Пример A-17.

pathchk

Производит проверку полного имени файла -- проверяет, доступны ли на чтение, каталоги в пути к файлу, и не превышает ли длина полного имени файла 255 символов. При несоблюдении одного из условий -- возвращает сообщение об ошибке.

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

dd

Эта немного непонятная и "страшная" команда ("data duplicator") изначально использовалась для переноса данных на магнитной ленте между микрокомпьютерами с ОС UNIX и майнфреймами IBM. Команда dd просто создает копию файла (или stdin/stdout), выполняя по пути некоторые преобразования. Один из вариантов: преобразование из ASCII в EBCDIC, [36] 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

Команда od (octal dump) производит преобразование ввода (или файла) в один или несколько форматов, в соответствии с указанными опциями. При отсутствии опций используется восьмеричный формат (опция - o). Эта команда полезна при просмотре или обработке файлов с двоичными данными, например /dev/urandom. См. Пример 9-27 и Пример 12-10.

hexdump

Выводит дамп двоичных данных из файла в восьмеричном, шестнадцатиричном, десятичном виде или в виде ASCII. Эту команду, с массой оговорок, можно назвать эквивалентом команды of od.

objdump

Отображает содержимое исполняемого или объектного файла либо в шестнадцатиричной форме, либо в виде дизассемблерного листинга (с ключом - 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

. . .

mcookie

Эта команда создает псевдослучайные шестнадцатиричные 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

Эта утилита производит преобразование величин из одних единиц измерения в другие. Как правило вызывается в интерактивном режиме, ниже приводится пример использования 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

Не команда, а клад, m4 -- это мощный фильтр обработки макроопределений, [37] фактически -- целый язык программирования. Изначально создававшаяся как препроцессор для RatFor, m4 оказалась очень полезной и как самостоятельная утилита. Фактически, m4 сочетает в себе функциональные возможности eval, tr, awk, и дополнительно предоставляет обширные возможности по созданию новых макроопределений.

Из за большого объема этот материал размещен на нескольких страницах:
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