Мендель Купер - Искусство программирования на языке сценариев командной оболочки
#+ в именах которых содержатся пробелы, заменяя пробелы символом подчеркивания.
Пример A-4. blank-rename: переименование файлов, чьи имена содержат пробелы
Это даже более простая версия предыдущего примера.
#! /bin/bash
# blank-rename.sh
#
# Заменяет пробелы символом подчеркивания в именах файлов в текущем каталоге.
ONE=1 # единственное или множественное число (см. ниже).
number=0 # Количество переименованных файлов.
FOUND=0 # Код завершения в случае успеха.
for filename in * # Перебор всех файлов в текущем каталоге.
do
echo "$filename" | grep -q " " # Проверить -- содержит ли имя файла
if [ $? -eq $FOUND ] #+ пробелы.
then
fname=$filename # Удалить путь из имени файла.
n=`echo $fname | sed -e "s/ /_/g"` # Заменить пробелы символом подчеркивания.
mv "$fname" "$n" # Переименование.
let "number += 1"
fi
done
if [ "$number" -eq "$ONE" ]
then
echo "$number файл переименован."
else
echo "Переименовано файлов: $number"
fi
exit 0
Пример A-5. encryptedpw: Передача файла на ftp-сервер, с использованием пароля
#!/bin/bash
# Модификация примера "ex72.sh", добавлено шифрование пароля.
# Обратите внимание: этот вариант все еще нельзя считать безопасным,
#+ поскольку в сеть пароль уходит в незашифрованном виде.
# Используйте "ssh", если вас это беспокоит.
E_BADARGS=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` имя_файла"
exit $E_BADARGS
fi
Username=bozo # Измените на свой.
pword=/home/bozo/secret/password_encrypted.file
# Файл, содержащий пароль в зашифрованном виде.
Filename=`basename $1` # Удалить путь из имени файла
Server="XXX"
Directory="YYY" # Подставьте фактические имя сервера и каталога.
Password=`cruft <$pword` # Расшифровка.
# Используется авторская программа "cruft",
#+ основанная на алгоритме "onetime pad",
#+ ее можно скачать с :
#+ Primary-site: ftp://ibiblio.org/pub/Linux/utils/file
#+ cruft-0.2.tar.gz [16k]
ftp -n $Server <<End-Of-Session
user $Username $Password
binary
bell
cd $Directory
put $Filename
bye
End-Of-Session
# ключ -n, команды "ftp", запрещает автоматический вход.
# "bell" -- звонок (звуковой сигнал) после передачи каждого файла.
exit 0
Пример A-6. copy-cd: Копирование компакт-дисков с данными
#!/bin/bash
# copy-cd.sh: copying a data CD
CDROM=/dev/cdrom # устройство CD ROM
OF=/home/bozo/projects/cdimage.iso # промежуточный файл
# /xxxx/xxxxxxx/ измените для своей системы.
BLOCKSIZE=2048
SPEED=2 # Можно задать более высокую скорость, если поддерживается.
echo; echo "Вставьте исходный CD, но *НЕ* монтируйте его."
echo "Нажмите ENTER, когда будете готовы. "
read ready # Ожидание.
echo; echo "Создается промежуточный файл $OF."
echo "Это может занять какое-то время. Пожалуйста подождите."
dd if=$CDROM of=$OF bs=$BLOCKSIZE # Копирование.
echo; echo "Выньте исходный CD."
echo "Вставьте чистую болванку CDR."
echo "Нажмите ENTER, когда будете готовы. "
read ready # Ожидание.
echo "Копируется файл $OF на болванку."
cdrecord -v -isosize speed=$SPEED dev=0,0 $OF
# Используется пакет Joerg Schilling -- "cdrecord" .
# http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html
echo; echo "Копирование завершено."
echo "Желаете удалить промежуточный файл (y/n)? " # Наверняка большой файл получился.
read answer
case "$answer" in
[yY]) rm -f $OF
echo "Файл $OF удален."
;;
*) echo "Файл $OF не был удален.";;
esac
echo
# Упражнение:
# Добавьте в оператор "case" возможность обработки, введенных пользователем, "yes" и "Yes".
exit 0
Пример A-7. Последовательности Коллаца (Collatz)
#!/bin/bash
# collatz.sh
# Широко известная последовательность Коллаца (Collatz) (гипотеза Коллаца).
# -------------------------------------------
# 1) Принимает из командной строки "начальное" целое число.
# 2) ЧИСЛО <--- НАЧАЛЬНОЕ ЗНАЧЕНИЕ
# 3) Вывести ЧИСЛО.
# 4) Если ЧИСЛО четное, разделить на 2,
# 5)+ Если не четное -- умножить на 3 и прибавить 1.
# 6) ЧИСЛО <--- РЕЗУЛЬТАТ
# 7) Повторить, начиная с п. 3, заданное число раз.
#
# Теоретически, такая последовательность должна сходиться,
#+ не зависимо от величины начального значения,
#+ к повторению циклов "4,2,1...",
#+ даже после значительных флуктуаций в самом начале.
MAX_ITERATIONS=200
# Для больших начальных значений (>32000), это значение придется увеличить.
h=${1:-$$} # Начальное значение
# если из командной строки ничего не задано, то берется $PID,
echo
echo "C($h) --- $MAX_ITERATIONS итераций"
echo
for ((i=1; i<=MAX_ITERATIONS; i++))
do
echo -n "$h "
# ^^^^^
# табуляция
let "remainder = h % 2"
if [ "$remainder" -eq 0 ] # Четное?
then
let "h /= 2" # Разделить на 2.
else
let "h = h*3 + 1" # Умножить на 3 и прибавить 1.
fi
COLUMNS=10 # Выводить по 10 значений в строке.
let "line_break = i % $COLUMNS"
if [ "$line_break" -eq 0 ]
then
echo
fi
done
echo
exit 0
Пример A-8. days-between: Подсчет числа дней между двумя датами
#!/bin/bash
# days-between.sh: Подсчет числа дней между двумя датами.
# Порядок использования: ./days-between.sh [M]M/[D]D/YYYY [M]M/[D]D/YYYY
ARGS=2 # Ожидается два аргумента из командной строки.
E_PARAM_ERR=65 # Ошибка в числе ожидаемых аргументов.
REFYR=1600 # Начальный год.
CENTURY=100
DIY=365
ADJ_DIY=367 # Корректировка на високосный год + 1.
MIY=12
DIM=31
LEAPCYCLE=4
MAXRETVAL=255 # Максимально возможное возвращаемое значение
# для положительных чисел.
diff= # Количество дней между датами.
value= # Абсолютное значение.
day= # день, месяц, год.
month=
year=
Param_Error () # Ошибка в пвраметрах командной строки.
{
echo "Порядок использования: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY"
echo " (даты должны быть после 1/3/1600)"
exit $E_PARAM_ERR
}
Parse_Date () # Разбор даты.
{
month=${1%%/**}
dm=${1%/**} # День и месяц.
day=${dm#*/}
let "year = `basename $1`" # Хотя это и не имя файла, но результат тот же.
}
check_date () # Проверка даты.
{
[ "$day" -gt "$DIM" ] || [ "$month" -gt "$MIY" ] || [ "$year" -lt "$REFYR" ] && Param_Error
# Выход из сценария при обнаружении ошибки.
# Используется комбинация "ИЛИ-списка / И-списка".
#
# Упражнение: Реализуйте более строгую проверку даты.
}
strip_leading_zero () # Удалить ведущий ноль
{
val=${1#0} # иначе Bash будет считать числа
return $val # восьмеричными (POSIX.2, sect 2.9.2.1).
}
day_index () # Формула Гаусса:
{ # Количество дней от 3 Янв. 1600 до заданной даты.
day=$1
month=$2
year=$3
let "month = $month - 2"
if [ "$month" -le 0 ]
then
let "month += 12"
let "year -= 1"
fi
let "year -= $REFYR"
let "indexyr = $year / $CENTURY"
let "Days = $DIY*$year + $year/$LEAPCYCLE - $indexyr + $indexyr/$LEAPCYCLE + $ADJ_DIY*$month/$MIY + $day - $DIM"
# Более подробное объяснение алгоритма вы найдете в
# http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
if [ "$Days" -gt "$MAXRETVAL" ] # Если больше 255,
then # то поменять знак
let "dindex = 0 - $Days" # чтобы функция смогла вернуть полное значение.
else let "dindex = $Days"
fi
return $dindex
}
calculate_difference () # Разница между двумя датами.
{
let "diff = $1 - $2" # Глобальная переменная.
}
abs () # Абсолютное значение
{ # Используется глобальная переменная "value".
if [ "$1" -lt 0 ] # Если число отрицательное
then # то
let "value = 0 - $1" # изменить знак,
else # иначе
let "value = $1" # оставить как есть.
fi
}
if [ $# -ne "$ARGS" ] # Требуется два аргумента командной строки.
then
Param_Error
fi
Parse_Date $1
check_date $day $month $year # Проверка даты.
strip_leading_zero $day # Удалить ведущие нули
day=$? # в номере дня и/или месяца.
strip_leading_zero $month
month=$?
day_index $day $month $year
date1=$?
abs $date1 # Абсолютное значение
date1=$value
Parse_Date $2
check_date $day $month $year
strip_leading_zero $day
day=$?
strip_leading_zero $month
month=$?
day_index $day $month $year
date2=$?
abs $date2 # Абсолютное значение
date2=$value
calculate_difference $date1 $date2
abs $diff # Абсолютное значение
diff=$value
echo $diff
exit 0
# Сравните этот сценарий с реализацией формулы Гаусса на C
# http://buschencrew.hypermart.net/software/datedif
Пример A-9. Создание "словаря"
#!/bin/bash
# makedict.sh [создание словаря]
# Модификация сценария /usr/sbin/mkdict.
# Авторские права на оригинальный сценарий принадлежат Alec Muffett.
#
# Этот модифицированный вариант включен в документ на основе