Глава 17. Встроенные документы
Встроенный документ (here
document) является специальной формой перенаправления ввода/вывода, которая
позволяет передать список команд интерактивной программе или
команде, например ftp, telnet или ex.
COMMAND <<InputComesFromHERE
...
InputComesFromHERE
Конец встроенного документа выделяется "строкой-ограничителем", которая
задается с помощью специальной последовательности символов
<<. Эта последовательность --
есть перенаправление вывода из файла на stdin программы или команды, что напоминает
конструкцию interactive-program
< command-file, где command-file содержит строки:
command #1
command #2
...
Сценарий, использующий "встроенный
документ" для тех же целей, может выглядеть
примерно так:
#!/bin/bash
interactive-program <<LimitString
command #1
command #2
...
LimitString
В качестве строки-ограничителя должна выбираться такая
последовательность символов, которая не будет встречаться в
теле "встроенного
документа".
Обратите внимание: использование встроенных
документов может иногда с успехом применяться и при
работе с неинтерактивными командами и утилитами.
Пример 17-1. dummyfile: Создание 2-х строчного
файла-заготовки
#!/bin/bash
# Неинтерактивное редактирование файла с помощью 'vi'.
# Эмуляция 'sed'.
E_BADARGS=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` filename"
exit $E_BADARGS
fi
TARGETFILE=$1
# Вставить 2 строки в файл и сохранить.
#--------Начало встроенного документа-----------#
vi $TARGETFILE <<x23LimitStringx23
i
Это строка 1.
Это строка 2.
^[
ZZ
x23LimitStringx23
#----------Конец встроенного документа-----------#
# Обратите внимание: ^[, выше -- это escape-символ
#+ Control-V <Esc>.
# Bram Moolenaar указывает, что этот скрипт может не работать с 'vim',
#+ из-за возможных проблем взаимодействия с терминалом.
exit 0
Этот сценарий, с тем же эффектом, мог бы быть реализован,
основываясь не на vi, а на
ex. Встроенные документы,
содержащие команды для ex,
стали настолько обычным делом, что их уже смело можно вынести в
отдельную категорию -- ex-сценарии.
Пример 17-2. broadcast: Передача сообщения всем,
работающим в системе, пользователям
#!/bin/bash
wall <<zzz23EndOfMessagezzz23
Пошлите, по электронной почте, ваш заказ на пиццу, системному администратору.
(Добавьте дополнительный доллар, если вы желаете положить на пиццу анчоусы или грибы.)
# Внимание: строки комментария тоже будут переданы команде 'wall' как часть текста.
zzz23EndOfMessagezzz23
# Возможно, более эффективно это может быть сделано так:
# wall <message-file
# Однако, встроенный документ помогает сэкономить ваши силы и время.
exit 0
Пример 17-3. Вывод многострочных сообщений с
помощью cat
#!/bin/bash
# Команда 'echo' прекрасно справляется с выводом однострочных сообщений,
# но иногда необходимо вывести несколько строк.
# Команда 'cat' и встроенный документ помогут вам в этом.
cat <<End-of-message
-------------------------------------
Это первая строка сообщения.
Это вторая строка сообщения.
Это третья строка сообщения.
Это четвертая строка сообщения.
Это последняя строка сообщения.
-------------------------------------
End-of-message
exit 0
#--------------------------------------------
# Команда "exit 0", выше, не позволить исполнить нижележащие строки.
# S.C. отмечает, что следующий код работает точно так же.
echo "-------------------------------------
Это первая строка сообщения.
Это вторая строка сообщения.
Это третья строка сообщения.
Это четвертая строка сообщения.
Это последняя строка сообщения.
-------------------------------------"
# Однако, в этом случае, двойные кавычки в теле сообщения, должны экранироваться.
Если строка-ограничитель встроенного документа начинается с
символа - (<<-LimitString), то это
приводит к подавлению вывода ведущих (начальных) символов
табуляции (но не пробелов). Это может оказаться полезным при
форматировании текста сценария для большей удобочитаемости.
Пример 17-4. Вывод многострочных сообщений с
подавлением символов табуляции
#!/bin/bash
# То же, что и предыдущий сценарий, но...
# Символ "-", начинающий строку-ограничитель встроенного документа: <<-
# подавляет вывод символов табуляции, которые могут встречаться в теле документа,
# но не пробелов.
cat <<-ENDOFMESSAGE
Это первая строка сообщения.
Это вторая строка сообщения.
Это третья строка сообщения.
Это четвертая строка сообщения.
Это последняя строка сообщения.
ENDOFMESSAGE
# Текст, выводимый сценарием, будет смещен влево.
# Ведущие символы табуляции не будут выводиться.
# Вышеприведенные 5 строк текста "сообщения" начинаются с табуляции, а не с пробелов.
exit 0
Встроенные документы поддерживают подстановку команд и
параметров. Что позволяет передавать различные параметры в тело
встроенного документа.
Пример 17-5. Встроенные документы и подстановка
параметров
#!/bin/bash
# Вывод встроенного документа командой 'cat', с использованием подстановки параметров.
# Попробуйте запустить сценарий без аргументов, ./scriptname
# Попробуйте запустить сценарий с одним аргументом, ./scriptname Mortimer
# Попробуйте запустить сценарий с одним аргументом, из двух слов, в кавычках,
# ./scriptname "Mortimer Jones"
CMDLINEPARAM=1 # Минимальное число аргументов командной строки.
if [ $# -ge $CMDLINEPARAM ]
then
NAME=$1 # Если аргументов больше одного,
# то рассматривается только первый.
else
NAME="John Doe" # По-умолчанию, если сценарий запущен без аргументов.
fi
RESPONDENT="автора этого сценария"
cat <<Endofmessage
Привет, $NAME!
Примите поздравления от $RESPONDENT.
# Этот комментарий тоже выводится (почему?).
Endofmessage
# Обратите внимание на то, что пустые строки тоже выводятся.
exit 0
Еще один пример сценария, содержащего встроенный документ и
подстановку параметров в его теле.
Пример 17-6. Передача пары файлов во входящий
каталог на "Sunsite"
#!/bin/bash
# upload.sh
# Передача пары файлов (Filename.lsm, Filename.tar.gz)
# на Sunsite (ibiblio.org).
E_ARGERROR=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` filename"
exit $E_ARGERROR
fi
Filename=`basename $1` # Отсечь имя файла от пути к нему.
Server="ibiblio.org"
Directory="/incoming/Linux"
# Вообще, эти строки должны бы не "зашиваться" жестко в сценарий,
# а приниматься в виде аргумента из командной строки.
Password="your.e-mail.address" # Измените на свой.
ftp -n $Server <<End-Of-Session
# Ключ -n запрещает автоматическую регистрацию (auto-logon)
user anonymous "$Password"
binary
bell # "Звякнуть" после передачи каждого файла
cd $Directory
put "$Filename.lsm"
put "$Filename.tar.gz"
bye
End-Of-Session
exit 0
Заключая строку-ограничитель в кавычки или экранируя ее,
можно запретить подстановку параметров в теле встроенного
документа.
Пример 17-7. Отключение подстановки
параметров
#!/bin/bash
# Вывод встроенного документа командой 'cat', с запретом подстановки параметров.
NAME="John Doe"
RESPONDENT="автора этого сценария"
cat <<'Endofmessage'
Привет, $NAME.
Примите поздравления от $RESPONDENT.
Endofmessage
# Подстановка параметров не производится, если строка ограничитель
# заключена в кавычки или экранирована.
# Тот же эффект дают:
# cat <<"Endofmessage"
# cat <<\Endofmessage
exit 0
Запрет на подстановку параметров позволяет выводить текст,
как говорится, "один к одному". Это обстоятельство
может использоваться, например, для автоматической генерации
сценариев или даже текстов программ на других языках
программирования.
Пример 17-8. Сценарий, который создает другой
сценарий
#!/bin/bash
# generate-script.sh
# Автор идеи: Albert Reiner.
OUTFILE=generated.sh # Имя нового сценария.
# -----------------------------------------------------------
# 'Встроенный документ' содержит тело создаваемого сценария.
(
cat <<'EOF'
#!/bin/bash
echo "Этот сценарий сгенерирован автоматически."
# Обратите внимание: поскольку действия происходят в подоболочке,
#+ мы не можем получить доступ к переменным родительской оболочки.
# Удостоверимся в этом...
echo "Файл сценария был назван: $OUTFILE" # Не работает.
a=7
b=3
let "c = $a * $b"
echo "c = $c"
exit 0
EOF
) > $OUTFILE
# -----------------------------------------------------------
# Заключение 'строки-ограничителя' предотвращает подстановку значений переменных
#+ в теле 'встроенного документа.'
# Что позволяет записать все строки в выходной файл "один к одному".
if [ -f "$OUTFILE" ]
then
chmod 755 $OUTFILE
# Дать право на исполнение.
else
echo "Не могу создать файл: \"$OUTFILE\""
fi
# Этот метод можно использовать для создания
#+ Makefile-ов, программ на языках C, Perl, Python
#+ и т.п..
exit 0
Допускается запись тела встроенного документа в
переменную.
variable=$(cat <<SETVAR
Это многострочная
переменная.
SETVAR)
echo "$variable"
Встроенные документы могут передаваться на вход функции,
находящейся в том же сценарии.
Пример 17-9. Встроенные документы и
функции
#!/bin/bash
# here-function.sh
GetPersonalData ()
{
read firstname
read lastname
read address
read city
read state
read zipcode
} # Это немного напоминает интерактивную функцию, но...
# Передать ввод в функцию.
GetPersonalData <<RECORD001
Bozo
Bozeman
2726 Nondescript Dr.
Baltimore
MD
21226
RECORD001
echo
echo "$firstname $lastname"
echo "$address"
echo "$city, $state $zipcode"
echo
exit 0
Встроенный документ можно передать "пустой
команде" :. Такая конструкция,
фактически, создает "анонимный" встроенный документ.
Пример 17-10. "Анонимный" Встроенный
Документ
#!/bin/bash
: <<TESTVARIABLES
${HOSTNAME?}${USER?}${MAIL?} # Если одна из переменных не определена, то выводится сообщение об ошибке.
TESTVARIABLES
exit 0
|
Подобную технику можно использовать для создания
"блочных
комментариев".
|
Пример 17-11. Блочный комментарий
#!/bin/bash
# commentblock.sh
: << COMMENTBLOCK
echo "Эта строка не будет выведена."
Эта строка комментария не начинается с символа "#".
Это еще одна строка комментария, которая начинается не с символа "#".
&*@!!++=
Эта строка не вызовет ошибки,
поскольку Bash проигнорирует ее.
COMMENTBLOCK
echo "Код завершения \"COMMENTBLOCK\" = $?." # 0
# Показывает, что ошибок не возникало.
# Такая методика создания блочных комментариев
#+ может использоваться для комментирования блоков кода во время отладки.
# Это экономит силы и время, т.к. не нужно втавлять символ "#" в начале каждой строки,
#+ а затем удалять их.
: << DEBUGXXX
for file in *
do
cat "$file"
done
DEBUGXXX
exit 0
|
Еще одно остроумное применение встроенных документов
-- встроенная справка к сценарию.
|
Пример 17-12. Встроенная справка к
сценарию
#!/bin/bash
# self-document.sh: сценарий со встроенной справкой
# Модификация сценария "colm.sh".
DOC_REQUEST=70
if [ "$1" = "-h" -o "$1" = "--help" ] # Request help.
then
echo; echo "Порядок использования: $0 [directory-name]"; echo
sed --silent -e '/DOCUMENTATIONXX$/,/^DOCUMENTATION/p' "$0" |
sed -e '/DOCUMENTATIONXX/d'; exit $DOC_REQUEST; fi
: << DOCUMENTATIONXX
Сценарий выводит сведения о заданном каталоге в виде таблице.
-------------------------------------------------------------
Сценарию необходимо передать имя каталога. Если каталог не
указан или он недоступен для чтения, то выводятся сведения
о текущем каталоге.
DOCUMENTATIONXX
if [ -z "$1" -o ! -r "$1" ]
then
directory=.
else
directory="$1"
fi
echo "Сведения о каталоге "$directory":"; echo
(printf "PERMISSIONS LINKS OWNER GROUP SIZE MONTH DAY HH:MM PROG-NAME\n" \
; ls -l "$directory" | sed 1d) | column -t
exit 0
|
Для встроенных документов, во время исполнения,
создаются временные файлы, но эти файлы удаляются после
открытия и недоступны для других процессов.
bash$ bash -c 'lsof -a -p $$ -d0' << EOF
> EOF
lsof 1213 bozo 0r REG 3,5 0 30386 /tmp/t1213-0-sh (deleted)
|
|
Некоторые утилиты не могут работать внутри
встроенных
документов.
|
|
Строка-ограничитель, закрывающая
встроенный документ, должна начинаться с первого
символа в строке. Перед ней не должно быть пробельных
символов. Аналогично, пробельные символы,
стоящие за строкой-ограничителем, могут дать
нежелательные побочные эффекты.
#!/bin/bash
echo "----------------------------------------------------------------------"
cat <<LimitString
echo "Это первая строка сообщения во встроенном документе."
echo "Это вторая строка сообщения во встроенном документе."
echo "Это последняя строка сообщения во встроенном документе."
LimitString
#^^^^Отступ перед строкой-ограничителем. Ошибка!
# Этот сценарий будет вести себя не так как вы ожидаете.
echo "----------------------------------------------------------------------"
# "Этот комментарий находится за пределами 'встроенного документа',
#+ и не должен выводиться.
echo "За пределами встроенного документа."
exit 0
echo "Держу пари, что эта строка не будет выведена." # Стоит после команды 'exit'.
|
Если какая либо задача не может быть решена с помощью
"встроенного документа",
то вам следует попробовать язык сценариев expect, который приспособлен для передачи
параметров на вход интерактивных программ.
Назад | Вперед
Содержание (общее) | Содержание раздела
Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту:
|