Мендель Купер - Искусство программирования на языке сценариев командной оболочки
# -p -- задает вид строки подсказки - приглашения к вводу (prompt).
# Использование этих ключей немного осложняется тем, что они должны следовать в определенном порядке.
Ключ -n, кроме всего прочего, позволяет команде read обнаруживать нажатие курсорных и некоторых других служебных клавиш.
Пример 11-5. Обнаружение нажатия на курсорные клавиши
#!/bin/bash
# arrow-detect.sh: Обнаружение нажатия на курсорные клавиши, и не только...
# Спасибо Sandro Magi за то что показал мне -- как.
# --------------------------------------------
# Коды клавиш.
arrowup='[A'
arrowdown='[B'
arrowrt='[C'
arrowleft='[D'
insert='[2'
delete='[3'
# --------------------------------------------
SUCCESS=0
OTHER=65
echo -n "Нажмите на клавишу... "
# Может потребоваться нажать на ENTER, если была нажата клавиша
# не входящая в список выше.
read -n3 key # Прочитать 3 символа.
echo -n "$key" | grep "$arrowup" #Определение нажатой клавиши.
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "О"."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "Insert"."
exit $SUCCESS
fi
echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
echo "Нажата клавиша "Delete"."
exit $SUCCESS
fi
echo " Нажата какая-то другая клавиша."
exit $OTHER
# Упражнения:
# ---------
# 1) Упростите сценарий, заменив множество if-ов
#+ одной конструкцией 'case'.
# 2) Добавьте определение нажатий на клавиши "Home", "End", "PgUp" и "PgDn".
Ключ -t позволяет ограничивать время ожидания ввода командой read (см. Пример 9-4).
Команда read может считывать значения для переменных из файла, перенаправленного на stdin. Если файл содержит не одну строку, то переменной будет присвоена только первая строка. Если команде read будет передано несколько переменных, то первая строка файла будет разбита, по пробелам, на несколько подстрок, каждая из которых будет записана в свою переменную. Будьте осторожны!
Пример 11-6. Чтение командой read из файла через перенаправление
#!/bin/bash
read var1 <data-file
echo "var1 = $var1"
# Первая строка из "data-file" целиком записывается в переменную var1
read var2 var3 <data-file
echo "var2 = $var2 var3 = $var3"
# Обратите внимание!
# Поведение команды "read" далеко от ожидаемого!
# 1) Произошел возврат к началу файла.
# 2) Вместо того, чтобы последовательно читать строки из файла,
# по числу переменных, первая строка файла была разбита на подстроки,
# разделенные пробелами, которые и были записаны в переменные.
# 3) В последнюю переменную была записана вся оставшаяся часть строки.
# 4) Если команде "read" будет передано большее число переменных, чем подстрок
# в первой строке файла, то последние переменные останутся "пустыми".
echo "------------------------------------------------"
# Эта проблема легко разрешается с помощью цикла:
while read line
do
echo "$line"
done <data-file
# Спасибо Heiner Steven за разъяснения.
echo "------------------------------------------------"
# Разбор строки, разделенной на поля
# Для задания разделителя полей, используется переменная $IFS,
echo "Список всех пользователей:"
OIFS=$IFS; IFS=: # В файле /etc/passwd, в качестве разделителя полей
# используется символ ":" .
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # перенаправление ввода.
IFS=$OIFS # Восстановление предыдущего состояния переменной $IFS.
# Эту часть кода написал Heiner Steven.
# Если переменная $IFS устанавливается внутри цикла,
#+ то отпадает необходимость сохранения ее первоначального значения
#+ во временной переменной.
# Спасибо Dim Segebart за разъяснения.
echo "------------------------------------------------"
echo "Список всех пользователей:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # перенаправление ввода.
echo
echo "Значение переменной $IFS осталось прежним: $IFS"
exit 0
Передача информации, выводимой командой echo, по конвейеру команде read, будет вызывать ошибку.
Тем не менее, передача данных по конвейеру от cat, кажется срабатывает.
cat file1 file2 |
while read line
do
echo $line
done
Файловая система
cd
Уже знакомая нам команда cd, изменяющая текущий каталог, может быть использована в случаях, когда некоторую команду необходимо запустить только находясь в определенном каталоге.
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
[взято из упоминавшегося ранее примера]
Команда cd с ключом -P (physical) игнорирует символические ссылки.
Команда "cd -" выполняет переход в каталог $OLDPWD -- предыдущий рабочий каталог.
Неожиданным образом выполняется команда cd, если ей передать, в качестве каталога назначения, два слэша.
bash$ cd //
bash$ pwd
//
Само собой разумеется, это должен был бы быть каталог /. Эта проблема наблюдается как в командной строке, так и в сценариях.
pwd
Выводит название текущего рабочего каталога (Print Working Directory) (см. Пример 11-7). Кроме того, имя текущего каталога хранится во внутренней переменной $PWD.
pushd, popd, dirs
Этот набор команд является составной частью механизма "закладок" на каталоги и позволяет перемещаться по каталогам вперед и назад в заданном порядке. Для хранения имен каталогов используется стек (LIFO -- "последний вошел, первый вышел").
pushd dir-name -- помещает имя текущего каталога в стек и осуществляет переход в каталог dir-name.
popd -- выталкивает, находящееся на вершине стека, имя каталога и одновременно осуществляет переход в каталог, оказавшийся на врешине стека.
dirs -- выводит содержимое стека каталогов (сравните с переменной $DIRSTACK). В случае успеха, обе команды -- pushd и popd автоматически вызывают dirs.
Эти команды могут оказаться весьма полезными, когда в сценарии нужно производить частую смену каталогов, но при этом не хочется жестко "зашивать" имена каталогов. Обратите внимание: содержимое стека каталогов постоянно хранится в переменной-массиве -- $DIRSTACK.
Пример 11-7. Смена текущего каталога
#!/bin/bash
dir1=/usr/local
dir2=/var/spool
pushd $dir1
# Команда 'dirs' будет вызвана автоматически (на stdout будет выведено содержимое стека).
echo "Выполнен переход в каталог `pwd`." # Обратные одиночные кавычки.
# Теперь можно выполнить какие либо действия в каталоге 'dir1'.
pushd $dir2
echo "Выполнен переход в каталог `pwd`."
# Теперь можно выполнить какие либо действия в каталоге 'dir2'.
echo "На вершине стека находится: $DIRSTACK."
popd
echo "Возврат в каталог `pwd`."
# Теперь можно выполнить какие либо действия в каталоге 'dir1'.
popd
echo "Возврат в первоначальный рабочий каталог `pwd`."