| Advanced Bash-Scripting Guide: Искусство программирования на языке сценариев командной оболочки; Версия 2.5 (15 февраля 2004) | ||
|---|---|---|
| Назад | Вперед | |
Встроенный документ (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, который приспособлен для передачи параметров на вход интерактивных программ.
| Назад | В начало документа | Вперед |
| Область применения | К началу раздела | Материал повышенной сложности |