Advanced Bash-Scripting Guide — различия между версиями

Материал из VAAL-WIKI
Перейти к: навигация, поиск
(Переменные и параметры. Введение.)
Строка 711: Строка 711:
  
 
== Переменные и параметры. Введение. ==
 
== Переменные и параметры. Введение. ==
 +
Переменные — это одна из основ любого языка программирования. Они участвуют в арифметических операциях, в синтаксическом анализе строк и совершенно необходимы для абстрагирования каких-либо величин с помощью символических имен. Физически переменные представляют собой ни что иное как участки памяти, в которые записана некоторая информация.
 
=== Подстановка переменных ===
 
=== Подстановка переменных ===
 +
Когда интерпретатор встречает в тексте сценария имя переменной, то он вместо него подставляет значение этой переменной. Поэтому ссылки на переменные называются подстановкой переменных.
 +
* <big>'''''$'''''</big>
 +
<br />
 +
Необходимо всегда помнить о различиях между именем переменной и ее значением. Если variable1 — это имя переменной, то $variable1 — это ссылка на ее значение. "Чистые" имена переменных, без префикса $, могут использоваться только при объявлении переменной, при присваивании переменной некоторого значения, при удалении (сбросе), при экспорте и в особых случаях — когда переменная представляет собой название сигнала. Присваивание может производится с помощью символа = (например: var1=27), инструкцией read и в заголовке цикла (for var2 in 1 2 3).
 +
<br />
 +
Заключение ссылки на переменную в двойные кавычки (" ") никак не сказывается на работе механизма подстановки. Этот случай называется "частичные кавычки", иногда можно встретить название "нестрогие кавычки". Одиночные кавычки (' ') заставляют интерпретатор воспринимать ссылку на переменную как простой набор символов, потому в одинарных кавычках операции подстановки не производятся. Этот случай называется "полные", или "строгие" кавычки.
 +
<br />
 +
Примечательно, что написание $variable фактически является упрощенной формой написания ${variable}. Более строгая форма записи ${variable} может с успехом использоваться в тех случаях, когда применение упрощенной формы записи порождает сообщения о синтаксических ошибках.
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Присваивание значений переменным и подстановка значений переменных.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# Присваивание значений переменным и подстановка значений переменных
 +
a=375
 +
hello=$a
 +
#-------------------------------------------------------------------------
 +
# Использование пробельных символов
 +
# с обеих сторон символа "=" присваивания недопустимо.
 +
# Если записать "VARIABLE =value",
 +
# то интерпретатор попытается выполнить команду "VARIABLE" с параметром "=value".
 +
# Если записать "VARIABLE= value",
 +
# то интерпретатор попытается установить переменную окружения "VARIABLE" в ""
 +
# и выполнить команду "value".
 +
#-------------------------------------------------------------------------
 +
echo hello # Это не ссылка на переменную, выведет строку "hello".
 +
echo $hello
 +
echo ${hello} # Идентично предыдущей строке.
 +
echo "$hello"
 +
echo "${hello}"
 +
echo
 +
hello="A B  C  D"
 +
echo $hello
 +
# A B C D
 +
echo "$hello" # A B C D
 +
# Здесь вы сможете наблюдать различия в выводе echo $hello и echo "$hello".
 +
# Заключение ссылки на переменную в кавычки сохраняет пробельные символы.
 +
echo
 +
echo '$hello' # $hello
 +
# Внутри одинарных кавычек не производится подстановка значений переменных,
 +
# т.е. "$" интерпретируется как простой символ.
 +
# Обратите внимание на различия, существующие между этими типами кавычек.
 +
hello= # Запись пустого значения в переменную.
 +
echo "\$hello (пустое значение) = $hello"
 +
# Обратите внимание: запись пустого значения — это не то же самое,
 +
# что сброс переменной, хотя конечный результат — тот же.
 +
# --------------------------------------------------------------
 +
# Допускается присваивание нескольких переменных в одной строке,
 +
# если они отделены пробельными символами.
 +
# Внимание! Это может снизить читабельность сценария и оказаться непереносимым.
 +
var1=variable1 var2=variable2 var3=variable3
 +
echo
 +
echo "var1=$var1 var2=$var2 var3=$var3"
 +
# Могут возникнуть проблемы с устаревшими версиями "sh".
 +
# --------------------------------------------------------------
 +
echo; echo
 +
numbers="один два три"
 +
other_numbers="1 2 3"
 +
# Если в значениях переменных встречаются пробелы,
 +
# то использование кавычек обязательно.
 +
echo "numbers = $numbers"
 +
echo "other_numbers = $other_numbers" # other_numbers = 1 2 3
 +
echo
 +
echo "uninitialized_variable = $uninitialized_variable"
 +
# Неинициализированная переменная содержит "пустое" значение.
 +
uninitialized_variable= # Объявление неинициализированной переменной (то же, что и присваивание пустого значения)
 +
echo "uninitialized_variable = $uninitialized_variable" # Переменная содержит "пустое" значение.
 +
uninitialized_variable=23 # Присваивание.
 +
unset uninitialized_variable # Сброс.
 +
echo "uninitialized_variable = $uninitialized_variable" # Переменная содержит "пустое" значение.
 +
echo
 +
exit 0
 +
</source>
 +
Неинициализированная переменная хранит "пустое" значение - не ноль! Использование неинициализированных переменных может приводить к ошибкам разного рода в процессе исполнения. Не смотря на это в арифметических операциях допускается использовать неинициализированные переменные.
 +
<source lang="bash">
 +
echo "$uninitialized" # (пустая строка)
 +
let "uninitialized += 5" # Прибавить 5.
 +
echo "$uninitialized" # 5
 +
# Заключение:
 +
# Неинициализированные переменные не имеют значения, однако
 +
# в арифметических операциях за значение таких переменных принимается число 0.
 +
# Это недокументированная (и возможно непереносимая) возможность.
 +
</source>
 
=== Присваивание значений переменным ===
 
=== Присваивание значений переменным ===
 +
* <big>'''''='''''</big>
 +
<br />
 +
Оператор присваивания (пробельные символы до и после оператора — недопустимы).
 +
Не путайте с операторами сравнения ''='' и ''-eq''!
 +
Обратите внимание: символ <big>'''''='''''</big> может использоваться как в качестве оператора присваивания, так и в качестве оператора сравнения, конкретная интерпретация зависит от контекста применения.
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Простое присваивание.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# Явные переменные
 +
echo
 +
# Когда перед именем переменной не употребляется символ '$'?
 +
# В операциях присваивания.
 +
# Присваивание
 +
a=879
 +
echo "Значение переменной \"a\" — $a."
 +
# Присваивание с помощью ключевого слова 'let'
 +
let a=16+5
 +
echo "Значение переменной \"a\" теперь стало равным: $a."
 +
echo
 +
# В заголовке цикла 'for' (своего рода неявное присваивание)
 +
echo -n "Значения переменной \"a\" в цикле: "
 +
for a in 7 8 9 11
 +
do
 +
echo -n "$a "
 +
done
 +
echo
 +
echo
 +
# При использовании инструкции 'read' (тоже одна из разновидностей присваивания)
 +
echo -n "Введите значение переменной \"a\" "
 +
read a
 +
echo "Значение переменной \"a\" теперь стало равным: $a."
 +
echo
 +
exit 0
 +
</source>
 +
<br />
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Присваивание значений переменным простое и замаскированное.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
a=23 # Простейший случай
 +
echo $a
 +
b=$a
 +
echo $b
 +
# Теперь немного более сложный вариант (подстановка команд).
 +
a=`echo Hello!` # В переменную 'a' попадает результат работы команды 'echo'
 +
echo $a
 +
# Обратите внимание на восклицательный знак (!) в подстанавливаемой команде
 +
# этот вариант не будет работать при наборе в командной строке,
 +
# поскольку здесь используется механизм "истории команд" BASH
 +
# Однако, в сценариях, механизм истории команд запрещен.
 +
a=`ls -l` # В переменную 'a' записывается результат работы команды 'ls -l'
 +
echo $a # Кавычки отсутствуют, удаляются лишние пробелы и пустые строки.
 +
echo
 +
echo "$a" # Переменная в кавычках, все пробелы и пустые строки сохраняются
 +
exit 0
 +
</source>
 +
<br />
 +
<source lang="bash">
 +
# Взято из /etc/rc.d/rc.local
 +
R=$(cat /etc/redhat-release)
 +
arch=$(uname -m)
 +
</source>
 +
 
=== Переменные Bash не имеют типа ===
 
=== Переменные Bash не имеют типа ===
 +
В отличие от большинства других языков программирования, Bash не производит разделения переменных по "типам". По сути, переменные Bash являются строковыми переменными, но, в зависимости от контекста, Bash допускает целочисленную арифметику с переменными. Определяющим фактором здесь служит содержимое переменных.
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Целое число или строка?'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# int-or-string.sh: Целое число или строка?
 +
a=2334 # Целое число.
 +
let "a += 1"
 +
echo "a = $a " # a = 2335
 +
echo # Всё ещё целое число.
 +
b=${a/23/BB} # замена "23" на "BB".
 +
# Происходит трансформация числа в строку.
 +
echo "b = $b"
 +
# b = BB35
 +
declare -i b # Явное указание типа здесь не поможет.
 +
echo "b = $b" # b = BB35
 +
let "b += 1" # BB35 + 1 =
 +
echo "b = $b" # b = 1
 +
echo
 +
c=BB34
 +
echo "c = $c" # c = BB34
 +
d=${c/BB/23} # замена "BB" на "23".
 +
# Переменная $d становится целочисленной.
 +
echo "d = $d" # d = 2334
 +
let "d += 1" # 2334 + 1 =
 +
echo "d = $d" # d = 2335echo
 +
echo
 +
# А что происходит с "пустыми" переменными?
 +
e=""
 +
echo "e = $e" # e =
 +
let "e += 1" # Арифметические операции допускают использование "пустых" переменных?
 +
echo "e = $e" # e = 1
 +
echo # "Пустая" переменная становится целочисленной.
 +
# А что происходит с необъявленными переменными?
 +
echo "f = $f" # f =
 +
let "f += 1" # Арифметические операции допустимы?
 +
echo "f = $f" # f = 1
 +
echo # Необъявленная переменная трансформируется в целочисленную.
 +
# Переменные Bash не имеют типов.
 +
exit 0
 +
</source>
 +
<br />
 +
Отсутствие типов — это и благословение и проклятие. С одной стороны — отсутствие типов делает сценарии более гибкими(чтобы повеситься — достаточно иметь веревку!) и облегчает чтение кода. С другой — является источником потенциальных ошибок и поощряет привычку к "неряшливому" программированию.
 +
Бремя отслеживания типа той или иной переменной полностью лежит на плечах программиста. Bash не будет делать это за вас!
 +
 
=== Специальные типы переменных ===
 
=== Специальные типы переменных ===
 +
* <big>'''''локальные переменные'''''</big>
 +
Переменные, область видимости которых ограничена блоком кода или телом функции (см. также локальные переменные в функциях)
 +
<br />
 +
<br />
 +
* <big>'''''переменные окружения'''''</big>
 +
Переменные, которые затрагивают командную оболочку и порядок взаимодействия с пользователем.
 +
В более общем контексте, каждый процесс имеет некоторое "окружение" (среду исполнения), т.е. набор переменных, к которым процесс может
 +
обращаться за получением определенной информации. В этом смысле командная оболочка подобна любому другому процессу.
 +
Каждый раз, когда запускается командный интерпретатор, для него создаются переменные, соответствующие переменным окружения. Изменение переменных или добавление новых переменных окружения заставляет оболочку обновить свои переменные, и все дочерние процессы (и команды, исполняемые ею)наследуют это окружение.
 +
Пространство, выделяемое под переменные окружения, ограничено. Создание слишком большого количества переменных
 +
окружения или одной переменной, которая занимает слишком большое пространство, может привести к возникновению определённых проблем.
 +
<source lang="bash">
 +
bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"
 +
bash$ du
 +
bash: /usr/bin/du: Argument list too long
 +
</source>
 +
Если сценарий изменяет переменные окружения, то они должны "экспортироваться", т.е. передаваться окружению, локальному по отношению к сценарию. Эта функция возложена на команду ''export''.
 +
Сценарий может экспортировать переменные только дочернему процессу, т.е.командам и процессам запускаемым из данного сценария.
 +
Сценарий, запускаемый из командной строки '''''не может''''' экспортировать переменные "наверх" командной оболочке. Дочерний процесс не может
 +
экспортировать переменные родительскому процессу.
 +
* <big>'''''позиционные параметры'''''</big>
 +
Аргументы, передаваемые скрипту из командной строки — $0, $1, $2, $3..., где $0 — это название файла сценария, $1 — это первый аргумент, $2 — второй, $3 — третий и так далее. Аргументы, следующие за $9, должны заключаться в фигурные скобки, например: ${10}, ${11}, ${12}.<br />
 +
Специальные переменные '''''$*''''' и '''''$@''''' содержат все позиционные параметры (аргументы командной строки).
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Позиционные параметры.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# Команда вызова сценария должна содержать по меньшей мере 10 параметров, например
 +
# ./scriptname 1 2 3 4 5 6 7 8 9 10
 +
MINPARAMS=10
 +
echo
 +
echo "Имя файла сценария: \"$0\"."
 +
# Для текущего каталога добавит ./
 +
echo "Имя файла сценария: \"`basename $0`\"."
 +
# Добавит путь к имени файла (см. 'basename')
 +
echo
 +
if [ -n "$1" ] # Проверяемая переменная заключена в кавычки.
 +
then
 +
echo "Параметр #1: $1" # необходимы кавычки для экранирования символа #
 +
fi
 +
if [ -n "$2" ]
 +
then
 +
echo "Параметр #2: $2"
 +
fi
 +
if [ -n "$3" ]
 +
then
 +
echo "Параметр #3: $3"
 +
fi
 +
if [ -n "${10}" ] # Параметры, следующие за $9 должны заключаться в фигурные скобки
 +
then
 +
echo "Параметр #10: ${10}"
 +
fi
 +
echo "-----------------------------------"
 +
echo "Все аргументы командной строки: "$*""
 +
if [ $# -lt "$MINPARAMS" ]
 +
then
 +
echo
 +
echo "Количество аргументов командной строки должно быть не менее $MINPARAMS !"
 +
fi
 +
echo
 +
exit 0
 +
</source>
 +
''Скобочная нотация'' позиционных параметров даёт довольно простой способ обращения к последнему аргументу, переданному в сценарий из командной строки. Такой способ подразумевает использование косвенной адресации.
 +
<source lang="bash">
 +
args=$# # Количество переданных аргументов.
 +
lastarg=${!args} # Обратите внимание: lastarg=${!$#} неприменимо.
 +
</source>
 +
В сценарии можно предусмотреть различные варианты развития событий, в зависимости отимени сценария. Для этого сценарий должен проанализировать аргумент $0 -- имя файласценария. Это могут быть и имена символических ссылок на файл сценария.
 +
Если сценарий ожидает передачи аргументов в командной строке, то при их отсутствии он получит "пустые" переменные, что может вызвать нежелательный
 +
побочный эффект. Один из способов борьбы с подобными ошибками — добавить дополнительный символ в обеих частях операции присваивания,
 +
где используются аргументы командной строки.
 +
<source lang="bash">
 +
variable1_=$1_# Это предотвратит появление ошибок, даже при отсутствии входного аргумента.
 +
critical_argument01=$variable1_
 +
# Дополнительные символы всегда можно "убрать" позднее.
 +
# Это может быть сделано примерно так:
 +
variable1=${variable1_/_/} # Побочный эффект возникает только если имя переменной # $variable1_ будет начинаться с символа "_".
 +
# Здесь используется один из вариантов подстановки параметров, обсуждаемых далее.
 +
# Отсутствие шаблона замены приводит к удалению.
 +
# Более простой способ заключается
 +
# в обычной проверке наличия позиционного параметра.
 +
if [ -z $1 ]; then
 +
exit $POS_PARAMS_MISSING
 +
fi
 +
</source>
 +
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''wh, whois выяснение имени домена.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# Команда 'whois domain-name' выясняет имя домена на одном из 3 серверов:
 +
# ripe.net, cw.net, radb.net
 +
# Разместите этот скрипт под именем 'wh' в каталоге /usr/local/bin
 +
# Требуемые символические ссылки:
 +
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
 +
# ln -s /usr/local/bin/wh /usr/local/bin/wh-cw
 +
# ln -s /usr/local/bin/wh /usr/local/bin/wh-radb
 +
if [ -z "$1" ]
 +
then
 +
echo "Порядок использования: `basename $0` [domain-name]"
 +
exit 65
 +
fi
 +
case `basename $0` in
 +
# Проверка имени скрипта и, соответственно, имени сервера
 +
"wh" ) whois $1@whois.ripe.net;;
 +
"wh-ripe") whois $1@whois.ripe.net;;
 +
"wh-radb") whois $1@whois.radb.net;;
 +
"wh-cw" ) whois $1@whois.cw.net;;
 +
* ) echo "Порядок использования: `basename $0` [domain-name]";;
 +
esac
 +
exit 0
 +
</source>
 +
Команда shift "сдвигает" позиционные параметры, в результате чего парметры "сдвигаются" на одну позицию влево.
 +
<br />$1 <--- $2, $2 <--- $3, $3 <--- $4, и т.д.<br />
 +
Прежний аргумент $1 теряется, но аргумент $0 (имя файла сценария) остаётся без изменений. Если вашему сценарию передаётся большое количество входных аргументов, то команда shift позволит вам получить доступ к аргументам, с порядковым номером больше 9, без использования {фигурных скобок}.
 +
<p style="text-align:center"><span style="color:green"> '''Пример'''</span>: <span style="color:red">'''''Использование команды shift.'''''</span></p>
 +
<source lang="bash">
 +
#!/bin/bash
 +
# Использование команды 'shift' с целью перебора всех аргументов командной строки.
 +
# Назовите файл с этим сценарием, например "shft",
 +
# и вызовите его с набором аргументов, например:
 +
# ./shft a b c def 23 skidoo
 +
until [ -z "$1" ] # До тех пор пока не будут разобраны все входные аргументы...
 +
do
 +
echo -n "$1 "
 +
shift
 +
done
 +
echo # Дополнительная пустая строка.
 +
exit 0
 +
</source>
 +
Команда ''shift'' может применяться и к входным аргументам функций.
 +
 
== Кавычки ==
 
== Кавычки ==
 
== Завершение и код завершения ==
 
== Завершение и код завершения ==

Версия 13:54, 23 июня 2017

bash (от англ. Bourne again shell, каламбур «Born again» shell — «возрождённый» shell) — усовершенствованная и модернизированная вариация командной оболочки Bourne shell. Одна из наиболее популярных современных разновидностей командной оболочки UNIX. Особенно популярна в среде Linux, где она часто используется в качестве предустановленной командной оболочки.

Bash — это командный процессор, работающий, как правило, в интерактивном режиме в текстовом окне. Bash также может читать команды из файла, который называется скриптом (или сценарием). Как и все Unix-оболочки, он поддерживает автодополнение имён файлов и директорий, подстановку вывода результата команд, переменные, контроль за порядком выполнения, операторы ветвления и цикла. Ключевые слова, синтаксис и другие основные особенности языка были заимствованы из sh. Другие функции, например, история, были скопированы из csh и ksh. Bash в основном удовлетворяет стандарту POSIX, но с рядом расширений[2].

Название «bash» является акронимом от Bourne-again-shell («ещё-одна-командная-оболочка-Борна») и представляет собой игру слов: Bourne-shell — одна из популярных разновидностей командной оболочки для UNIX (sh), автором которой является Стивен Борн (1978), усовершенствована в 1987 году Брайаном Фоксом. Фамилия Bourne (Борн) перекликается с английским словом born, означающим «родившийся», отсюда: рождённая-вновь-командная оболочка.

В сентябре 2014 года в bash была обнаружена широко эксплуатируемая уязвимость Bashdoor.

Содержание

Часть первая. Ввведение

Shell — это командная оболочка. Но это не просто промежуточное звено между пользователем и операционной системой, это ещё и мощный язык программирования. Программы на языке shell называют сценариями, или скриптами. Фактически, из скриптов доступен полный набор команд, утилит и программ Unix. Если этого недостаточно, то к вашим услугам внутренние команды shell — условные операторы, операторы циклов и пр., которые увеличивают мощь и гибкость сценариев. Shell-скрипты исключительно хороши при программировании задач администрирования системы и др., которые не требуют для своего создания полновесных языков программирования.

Зачем необходимо знание языка Shell?


Знание языка командной оболочки является залогом успешного решения задач администрирования системы. Даже если вы не предполагаете заниматься написанием своих сценариев. Во время загрузки Linux выполняется целый ряд сценариев из /etc/rc.d, которые настраивают конфигурацию операционной системы и запускают различные сервисы, поэтому очень важно четко понимать эти скрипты и иметь достаточно знаний, чтобы вносить в них какие либо изменения. Язык сценариев легок в изучении, в нем не так много специфических операторов и конструкций.

Синтаксис языка достаточно прост и прямолинеен, он очень напоминает команды, которые приходится вводить в командной строке. Короткие скрипты практически не нуждаются в отладке, и даже отладка больших скриптов отнимает весьма незначительное время. Shell-скрипты очень хорошо подходят для быстрого создания прототипов сложных приложений, даже не смотря на ограниченный набор языковых конструкций и определенную "медлительность". Такой метод позволяет детально проработать структуру будущего приложения, обнаружить возможные"ловушки" и лишь затем приступить к кодированию на C, C++, Java, или Perl.

Скрипты возвращают нас к классической философии Unix — "разделяй и властвуй" т.е. разделение сложного проекта на ряд простых подзадач. Многие считают такой подход наилучшим или, по меньшей мере, наиболее эстетичным способом решения возникающих проблем, нежели использование нового поколения языков — "всё-в-одном", таких как Perl. Для каких задач неприменимы скрипты:

  • для ресурсоемких задач, особенно когда важна скорость исполнения (поиск, сортировка и т.п.)
  • для задач, связанных с выполнением математических вычислений, особенно это касается вычислений с плавающей запятой, вычислений с повышенной точностью, комплексных чисел(для таких задач лучше использовать C++ или FORTRAN)
  • для кросс-платформенного программирования (для этого лучше подходит язык C)
  • для сложных приложений, когда структурирование является жизненной необходимостью(контроль за типами переменных, прототипами функций и т.п.)
  • для целевых задач, от которых может зависеть успех предприятия
  • когда во главу угла поставлена безопасность системы, когда необходимо обеспечить целостность системы и защитить её от вторжения, взлома и вандализма
  • для проектов, содержащих компоненты, очень тесно взаимодействующие между собой
  • для задач, выполняющих огромный объем работ с файлами
  • для задач, работающих с многомерными массивами
  • когда необходимо работать со структурами данных, такими как связанные списки или деревья
  • когда необходимо предоставить графический интерфейс с пользователем (GUI)
  • когда необходим прямой доступ к аппаратуре компьютера
  • когда необходимо выполнять обмен через порты ввода-вывода или сокеты
  • когда необходимо использовать внешние библиотеки
  • для проприетарных, "закрытых" программ (скрипты представляют из себя исходные тексты программ, доступные для всеобщего обозрения)


Если выполняется хотя бы одно из вышеперечисленных условий, то вам лучше обратиться к более мощным скриптовым языкам программирования, например Perl, Tcl, Python, Ruby или к высокоуровневым компилирующим языкам — C, C++ или Java. Но даже в этом случае, создание прототипа приложения на языке shell может существенно облегчить разработку. Название BASH — это аббревиатура от "Bourne-Again Shell" и игра слов от, ставшего уже классикой,"Bourne Shell" Стефена Бурна (Stephen Bourne). В последние годы BASH достиг такой популярности,что стал стандартной командной оболочкой de facto для многих разновидностей Unix. Большинство принципов программирования на BASH одинаково хорошо применимы и в других командных оболочках, таких как Korn Shell (ksh), от которой Bash позаимствовал некоторые особенности, и CShell и его производных(Примечательно, что C Shell не рекомендуется к использованию из-за отдельных проблем, отмеченных Томом Кристиансеном (Tom Christiansen) в октябре 1993 года на Usenet post).

Далее, в тексте документа вы найдете большое количество примеров скриптов, иллюстрирующихвозможности shell. Все примеры -- работающие. Они были протестированы, причем некоторые из них могут пригодиться в повседневной работе. Уважаемый читатель может "поиграть" с рабочим кодом скриптов, сохраняя их в файлы, с именами scriptname.sh.

Не забудьте выдать этим файлам право на исполнение (chmod u+rx scriptname.sh), после чего сценарии можно будет запустить на исполнение и проверить результат их работы. Вам следует помнить, что описание некоторых примеров следует после исходного кода этого примера, поэтому, прежде чем запустить сценарий у себя — ознакомьтесь с его описанием. Скрипты были написаны автором книги, если не оговаривается иное.

Для начала о Sha-Bang


В простейшем случае, скрипт — это ни что иное, как простой список команд системы, записанный в файл. Создание скриптов поможет сохранить ваше время и силы, которые тратятся на ввод последовательности команд всякий раз, когда необходимо их выполнить.

Пример. cleanup: Сценарий очистки лог-файлов в /var/log

 # cleanup
 # Для работы сценария требуются права root.
 cd /var/log
 cat /dev/null > messages
 cat /dev/null > wtmp
 echo "Лог-файлы очищены."

Пример. cleanup: Расширенная версия предыдущего сценария.

#!/bin/bash
# cleanup, version 2
# Для работы сценария требуются права root.
LOG_DIR=/var/log
ROOT_UID=0 # Только пользователь с $UID 0 имеет привилегии root.
LINES=50 # Количество сохраняемых строк по-умолчанию.
E_XCD=66 # Невозможно сменить каталог?
E_NOTROOT=67 # Признак отсутствия root-привилегий.
 
if [ "$UID" -ne "$ROOT_UID" ]; then 
 echo "Для работы сценария требуются права root." 
exit $E_NOTROOT
fi
 
if [ -n "$1" ]; then 
# Проверка наличия аргумента командной строки
 lines=$1
else lines=$LINES # Значение по-умолчанию, если число не задано в командной строке
fi
 
# Stephane Chazelas предложил следующее,
#+ для проверки корректности аргумента, переданного из командной строки,
#+ правда это достаточно сложно для данного руководства.
#
# E_WRONGARGS=65 # Не числовой аргумент
#
# case "$1" in
# "" ) lines=50;;
# *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
# * ) lines=$1;;
# esac
#
#* Конец проверки корректности аргумента
 
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ]; then 
# или if [ "$PWD" != "$LOG_DIR" ]
# не в /var/log?
 echo "Невозможно перейти в каталог $LOG_DIR." 
exit $E_XCD
fi # Проверка каталога перед очисткой лог-файлов.
# более эффективный вариант:
#
# cd /var/log || {
# echo "Невозможно перейти в требуемый каталог." >&2# exit $E_XCD;
# }
 
tail -$lines messages > mesg.temp # Сохранить последние строки в лог-файле.
mv mesg.temp messages
# cat /dev/null > messages
#* Необходимость этой команды отпала, поскольку очистка выполняется выше.
cat /dev/null > wtmp # команды ': > wtmp' и '> wtmp' имеют тот же эффект.
echo "Лог-файлы очищены."
exit 0 # Возвращаемое значение 0
#+ указывает на успешное завершение работы сценария.


Если вы не желаете полностью вычищать системные логи, то выше представлена улучшенная версия предыдущего сценария. Здесь сохраняются последние несколько строк (по-умолчанию — 50). Если файл сценария начинается с последовательности #!, которая в мире Unix называется sha-bang, то это указывает системе какой интерпретатор следует использовать для исполнения сценария. Это двухбайтовая последовательность, или — специальный маркер, определяющий тип сценария, в данном случае — сценарий командной оболочки (см. man magic). Более точно, sha-bang определяет интерпретатор, который вызывается для исполнения сценария, это может быть командная оболочка(shell), иной интерпретатор или утилита:

#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f


Каждая, из приведенных выше сигнатур, приводит к вызову различных интерпретаторов, будь то /bin/sh — командный интерпретатор по-умолчанию (bash для Linux-систем), либо иной. При переносе сценариев с сигнатурой #!/bin/sh на другие Unix системы, где в качестве командного интерпретатора задан другой shell, вы можете лишиться некоторых особенностей, присущих bash. Поэтому такие сценарии должны быть POSIX совместимыми.
Обратите внимание на то, что сигнатура должна указывать правильный путь к интерпретатору, впротивном случае вы получите сообщение об ошибке -- как правило это "Command not found". Сигнатура #! может быть опущена, если вы не используете специфичных команд. Во втором примере(см. выше) использование сигнатуры #! обязательно, поскольку сценарий использует специфичную конструкцию присваивания значения переменной lines=50. Ещё раз замечу, что сигнатура #!/bin/sh вызывает командный интерпретатор по-умолчанию — /bin/bash в Linux-системах.
В данном руководстве приветствуется модульный подход к построению сценариев. Записывайте, собирайте свою коллекцию участков кода, который может вам встретиться. В конечном итоге вы соберете свою "библиотеку" подпрограмм, которые затем сможете использовать при написании своих сценариев. Например, следующий отрывок сценария проверяет количество аргументов в командной строке:

if [ $# -ne Number_of_expected_args ]; then 
 echo "Usage: `basename $0` whatever" 
 exit $WRONG_ARGS
fi

Запуск сценария

Запустить сценарий можно командой sh scriptname или bash scriptname. (Не рекомендуется запуск сценария командой sh < scriptname, поскольку это запрещает использование устройства стандартного ввода stdin в скрипте). Более удобный вариант — сделать файл скрипта исполняемым, командой chmod.
Это:

chmod 555 scriptname (выдача прав на чтение/исполнение любому пользователю в системе)

или

chmod +rx scriptname (выдача прав на чтение/исполнение любому пользователю в системе)
chmod u+rx scriptname (выдача прав на чтение/исполнение только "владельцу" скрипта)


После того, как вы сделаете файл сценария исполняемым, вы можете запустить его примерно такой командой ./scriptname. Если, при этом, текст сценария начинается с корректной сигнатуры ("sha-bang"), то для его исполнения будет вызван соответствующий интерпретатор.
И наконец, завершив отладку сценария, вы можете поместить его в каталог /usr/local/bin(естественно, что для этого вы должны обладать правами root), чтобы сделать его доступным для себя и других пользователей системы. После этого сценарий можно вызвать, просто напечатав название файла в командной строке и нажав клавишу [ENTER].

Упражнения

1) Системные администраторы часто создают скрипты для автоматизации своего труда. Подумайте, для выполнения каких задач могут быть написаны сценарии.
2) Напишите сценарий, который выводит дату, время, список зарегистрировавшихся пользователей, и uptime системы и сохраняет эту информацию в системном журнале.

Часть вторая. Основы

Служебные символы

Служебные символы, используемые в текстах сценариев:

  • #Комментарии.


Строки, начинающиеся с символа # (за исключением комбинации #!) являются комментариями.

# Эта строка — комментарий.

Комментарии могут располагаться и в конце строки с исполняемым кодом.

echo "Далее следует комментарий." # Это комментарий.

Комментариям могут предшествовать пробелы (пробел, табуляция).

# Перед комментарием стоит символ табуляции.

Исполняемые команды не могут следовать за комментарием в той же самой строке. Пока что еще не существует способа отделения комментария от "исполняемого кода", следующего за комментарием в той же строке.
Само собой разумеется, экранированный символ # в операторе echo не воспринимается как начало комментария. Более того, он может использоваться в операциях подстановки параметров и в константных числовых выражениях.

echo "Символ # не означает начало комментария."
echo 'Символ # не означает начало комментария.'
echo Символ \# не означает начало комментария.
echo А здесь символ # означает начало комментария.
echo ${PATH#*:} # Подстановка — не комментарий.
echo $(( 2#101011 )) # База системы счисления — не комментарий.

Двойные кавычки("), одинарная кавычка(') и символ обратного "слеша"(\) экранируют действие символа #. В операциях поиска по шаблону символ # так же не воспринимается как начало комментария.

  • ;Разделитель команд. [Точка-с-запятой]

Позволяет записывать две и более команд в одной строке.

echo hello; echo there 
if [ -x "$filename" ]; then # Обратите внимание: "if" И "then" разделены точкой с запятой. 
# Почему? 
echo "Файл $filename найден."; cp $filename $filename.bak
  else echo "Файл $filename не найден."; touch $filename
fi; 
echo "Конец."

Следует отметить, что символ ; иногда так же как и # необходимо экранировать.

  • ;;Ограничитель в операторе выбора case. [Двойная-точка-с-запятой]
case "$variable" 
in 
abc) echo "$variable = abc" ;;
xyz) echo "$variable = xyz" ;;
esac
  • .Команда "точка"(.)

Эквивалент команды source. Это встроенная команда bash. "точка" может являться частью имени файла. Если имя файла начинается с точки, то это "скрытый" файл, т.е. команда ls при обычных условиях его не отображает:

Ls hidden.PNG


Если подразумевается имя каталога, то одна точка означает текущий каталог и две точки — каталог уровнем выше или родительский каталог:

Parent dir.PNG

Символ . ("точка") довольно часто используется для обозначения каталога назначения в операциях копирования/перемещения файлов.

bash$ cp /home/bozo/current_work/junk/* .

Символ . ("точка") в операциях поиска. При выполнении поиска по шаблону, в регулярных выражениях, символ . ("точка") обозначает одиночный символ.

  • "Двойные кавычки(")

В строке "STRING", ограниченной двойными кавычками не выполняется интерпретация большинства служебных символов, которые могут находиться в строке.

  • 'Одинарные кавычки(')

Одинарные кавычки 'STRING' экранируют все служебные символы в строке STRING. Это более строгая форма экранирования.

  • ,Запятая(,)

Оператор запятая(,) используется для вычисления серии арифметических выражений. Вычисляются все выражения, но возвращается результат последнего выражения.

let "t2 = ((a = 9, 15 / 3))" # Присваивает значение переменной "a" и вычисляет "t2".
  • \Обратный слэш(\)

Комбинация \X "экранирует" символ X. Аналогичный эффект имеет комбинация с "одинарными кавычками", т.е. 'X'. Символ \ может использоваться для экранирования кавычек " и '.

  • /Слэш(/)

Разделитель, используемый в указании пути к каталогам и файлам. Слэш отделяет элементы пути к каталогам и файлам (например, /home/bozo/projects/Makefile). В арифметических операциях — это оператор деления.

  • `Обратная кавычка(`)

С помощью символа обратной кавычки(`) осуществляется подстановка команд. Обратные кавычки могут использоваться для записи в переменную команды `command`.

  • :Двоеточие(:)

Пустая команда. Это эквивалент операции "NOP" (no op, нет операции). Может рассматриваться как синоним встроенной команды true. Команда ":" так же является встроенной командой Bash, которая всегда возвращает "true" (0).

:
echo $? # 0

Бесконечный цикл:

while :
do 
operation-1 
operation-2 
... 
operation-n
done
# То же самое:
# while true
# do
# ...
# done

Символ-заполнитель в условном операторе if/then:

if condition
then : # Никаких действий не производится и управление передаётся дальше
else 
 take-some-action
fi

Как символ-заполнитель в операциях, которые предполагают наличие двух операндов:

: ${username=`whoami`}
# ${username=`whoami`} без символа : выдает сообщение об ошибке,
# если "username" не является командой...

Как символ-заполнитель для оператора вложенного документа. В операциях с подстановкой параметров:

: ${HOSTNAME?} ${USER?} ${MAIL?}
#Вывод сообщения об ошибке, если одна или более переменных не определены.

В операциях замены подстроки с подстановкой значений переменных. В комбинации с оператором > (оператор перенаправления вывода), усекает длину файла до нуля. Если указан несуществующий файл — то он создаётся.

: > data.xxx # Файл "data.xxx" — пуст
# Тот же эффект имеет команда cat /dev/null >data.xxx
# Однако в данном случае не производится создание нового процесса, поскольку ":" является встроенной командой.

В комбинации с оператором >> — если задано имя существующего файла, то эта комбинация на него никак не влияет (: >> target_file). Если задано имя несуществующего файла, то он создаётся. Вышеизложенное применимо только к обычным файлам и неприменимо к конвейерам, символическим ссылкам и другим специальным файлам. Символ : может использоваться для создания комментариев, хотя и не рекомендуется. Если строка комментария начинается с символа #, то такая строка не проверяется интерпретатором на наличие ошибок. Однако в случае оператора : это не так.

: Это комментарий, который генерирует сообщение об ошибке, ( if [ $x -eq 3] ).

Символ : может использоваться как разделитель полей в /etc/passwd и переменной $PATH.

bash$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games
  • !Восклицательный знак(!)

Инверсия (или логическое отрицание) используемое в условных операторах. Оператор ! инвертирует код завершения команды, к которой он применен. Также используется для логического отрицания в операциях сравнения, например, операция сравнения "равно"(=), при использовании оператора отрицания, преобразуется в операцию сравнения — "не равно" ( != ). Символ ! является зарезервированным ключевым словом BASH. В некоторых случаях символ ! используется для косвенного обращения к переменным. Кроме того, из командной строки оператор ! запускает механизм историй Bash. Примечательно, что этот механизм недоступен из сценариев(т.е. исключительно из командной строки).

  • *Символ "звёздочка"(*)

Символ * ("звёздочка") служит "шаблоном" для подстановки в имена файлов. Одиночный символ * означает любое имя файла в заданном каталоге.

bash$ echo *
abs-book.sgml add-drive.sh agram.sh alias.sh

В регулярных выражениях токен * представляет любое количество (в том числе и 0) символов. В арифметических выражениях символ * обозначает операцию умножения. Двойная звёздочка (два символа звёздочки, следующих подряд друг за другом — **),обозначает операцию возведения в степень.

  • ?Вопросительный знак(?)

Оператор проверки условия. В некоторых выражениях символ ? служит для проверки выполнения условия. В конструкциях с двойными скобками, символ ? подобен трёхместному оператору языка C. В выражениях с подстановкой параметра, символ ? проверяет — установлена ли переменная. Символ ? обозначает одиночный символ при подстановке в имена файлов. В регулярных выражениях служит для обозначения одиночного символа.

  • $Символ доллара($)

Символ $ предшествующий имени переменной, указывает на то, что будет получено значение переменной:

var1=5
var2=23skidoo
echo $var1 # 5
echo $var2 # 23skidoo

В регулярных выражениях, символ $ обозначает конец строки. ${ } — подстановка параметра. $*,$@ — параметры командной строки. $? — Переменная "$?" хранит код завершения последней выполненной команды, функции или сценария. $$ — Переменная "$$" хранит id процесса сценария.

  • ( )Круглые скобки(())

Команды, заключённые в круглые скобки (( )) исполняются в дочернем процессе — subshell.

(a=hello; echo $a)

Переменные, создаваемые в дочернем процессе не видны в "родительском" сценарии. Родительский процесс-сценарий, не может обращаться к переменным, создаваемым в дочернем процессе.

a=123
( a=321; )
echo "a = $a" # a = 123
#Переменная "a" в скобках подобна локальной переменной.

Круглые скобки (()) также используются для инициализации массивов:

Array=(element1 element2 element3)

(( )) — двойные круглые скобки.Вычисляется целочисленное выражение, заключенное между двойными круглыми скобками (( )).

  • {}Фигурные скобки({})

Команда интерпретируется как список команд, разделённых точкой с запятой, с вариациями, представленными в фигурных скобках. При интерпретации имён файлов (подстановка) используются параметры, заключённые в фигурные скобки.

grep Linux file*.{txt,htm*}# Поиск всех вхождений слова "Linux"
# в файлах "fileA.txt", "file2.txt", "fileR.html", "file-87.htm", и пр.

Использование неэкранированных или неокавыченных пробелов внутри фигурных скобок недопустимо!

echo {file1,file2}\ :{\ A," B",' C'}
file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{} — Известен так же как "вложенный блок", эта конструкция, фактически, создаёт анонимную функцию. Однако, в отличии от обычных функций, переменные, создаваемые во вложенных блоках кода, доступны объемлющему сценарию.

bash$ { local a; a=123; }
bash: local: can only be used in a function
a=123
{ a=321; }
echo "a = $a"
# a = 321 (значение, присвоенное во вложенном блоке кода)

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

Пример.: Вложенные блоки и перенаправление ввода-вывода.

#!/bin/bash
# Чтение строк из файла /etc/fstab.
File=/etc/fstab
{read line1
read line2
} < $File
echo "Первая строка в $File :"
echo "$line1"
echo
echo "Вторая строка в $File :"
echo "$line2"
exit 0

Пример.: Сохранение результата исполнения вложенного блока в файл

#!/bin/bash
# rpm-check.sh
# Запрашивает описание rpm-архива, список файлов, и проверяется возможность установки.
# Результат сохраняется в файле.
#
# Этот сценарий иллюстрирует порядок работы со вложенными блоками кода.
SUCCESS=0
E_NOARGS=65
if [ -z "$1" ]; then
 echo "Порядок использования: `basename $0` rpm-file" 
 exit $E_NOARGS
fi
{
   echo 
   echo "Описание архива:" 
   rpm -qpi $1 # Запрос описания.
   echo
   echo "Список файлов:"
   rpm -qpl $1 # Запрос списка.
   echo
   rpm -i --test $1 # Проверка возможности установки.
   if [ "$?" -eq $SUCCESS ]; then
      echo "$1 может быть установлен."
   else
      echo "$1 — установка невозможна!"
   fi
   echo
}> "$1.test" # Перенаправление вывода в файл.
echo "Результаты проверки rpm-архива находятся в файле $1.test"
# За дополнительной информацией по ключам команды rpm см. man rpm.
exit 0

В отличие от групп команд в (круглых скобках), описаных выше, вложенные блоки кода, заключённые в {фигурные скобки} исполняются в пределах того же процесса, что и сам скрипт (т.е. не вызывают запуск дочернего процесса — subshell)!
{ } \;pathname, полное имя файла (т.е. путь к файлу и его имя). Чаще всего используется совместно с командой find. Обратите внимание на то, что символ ; , которым завершается ключ -exec команды find, экранируется обратным слэшем. Это необходимо, чтобы предотвратить его интерпретацию.

  • [ ]Квадратные скобки([ ])

Проверка истинности выражения, заключенного в квадратные скобки [ ]. Примечательно, что [ является частью встроенной команды test (и её синонимом), и не имеет никакого отношения к "внешней" утилите /usr/bin/test.
[[ ]] — Проверка истинности выражения, заключенного между [[ ]] (зарезервированное слово интерпретатора).
[ ] — Элемент массива. При работе с массивами в квадратных скобках указывается порядковый номер того элемента массива, к которому производится обращение.

Array[1]=slot_1
echo ${Array[1]}

[ ] — диапазон символов. В регулярных выражениях, в квадратных скобках задаётся диапазон искомых символов.

  • >, &>, >&, >>, < — Перенаправление.


Конструкция scriptname > filename перенаправляет вывод scriptname в файл filename. Если файл filename уже существовал, то его прежнее содержимое будет утеряно.
Конструкция command &> filename перенаправляет вывод команды command, как с stdout, так и с stderr, в файл filename.
Конструкция command >&2 перенаправляет вывод с stdout на stderr.
Конструкция scriptname >> filename добавляет вывод scriptname к файлу filename. Если задано имя несуществующего файла, то он создаётся. << — Перенаправление ввода на встроенный документ.
(command)>, <(command) — Подстановка процесса. В операциях сравнения, символы "<" и ">" обозначают операции сравнения строк. А так же операции сравнения целых чисел. <, > — Посимвольное ASCII-сравнение.

veg1=carrots
veg2=tomatoes
if [[ "$veg1" < "$veg2" ]]; then
 echo "Не смотря на то, что в словаре слово $veg1 предшествует слову $veg2,"
 echo "это никак не отражает мои кулинарные предпочтения."
else
 echo "Интересно. Каким словарем вы пользуетесь?"
fi

\<,\> — границы отдельных слов в регулярных выражениях.

bash$ grep '\<the\>' textfile
  • | — Конвейер(pipe).

Передает вывод предыдущей команды на ввод следующей или на вход командного интерпретатора shell. Этот метод часто используется для связывания последовательности команд в единую цепочку.

echo ls -l | sh
# Передаёт вывод "echo ls -l" командному интерпретатору shell,
# тот же результат даёт простая команда "ls -l".
cat *.lst | sort | uniq
# Объединяет все файлы ".lst", сортирует содержимое и удаляет повторяющиеся строки.
Конвейеры (еще их называют каналами) — это классический способ взаимодействия процессов, с помощью которого stdout одного процесса перенаправляется на stdin другого. Обычно используется совместно с командами вывода, такими как cat или echo, от которых поток данных поступает в "фильтр" (команда, которая на входе получает данные, преобразует их и обрабатывает).
cat $filename | grep $search_word

В конвейер могут объединяться и сценарии на языке командной оболочки.

#!/bin/bash
# uppercase.sh : Преобразование вводимых символов в верхний регистр.
tr 'a-z' 'A-Z'
# Диапазоны символов должны быть заключены в кавычки
# чтобы предотвратить порождение имен файлов от однобуквенных имен файлов.
exit 0

А теперь попробуем объединить в конвейер команду ls -l с этим сценарием:

Uppercase 0.PNG

Выход stdout каждого процесса в конвейере должен читаться на входе stdin последующим, в конвейере, процессом. Если этого не делается, то поток данных блокируется, в результате конвейер будет работать не так как ожидается!

cat file1 file2 | ls -l | sort # Вывод команды "cat file1 file2" будет утерян.

Конвейер исполняется в дочернем процессе, а посему — не имеет доступа к переменным сценария.

variable="initial_value"
echo "new_value" | read variable 
echo "variable = $variable" # variable = initial_value

Если одна из команд в конвейере завершается аварийно, то это приводит каварийному завершению работы всего конвейера!
>| — принудительное перенаправление, даже если установлен ключ noclobber option.

  • || логическая операция OR(логическое ИЛИ).

В опрециях проверки условий, оператор || возвращает 0 (success), если один из операндов имеет значение true (ИСТИНА).

  • & Амперсанд

Выполнение задачи в фоне. Команда, за которой стоит &, будет исполняться в фоновом режиме.

bash$ sleep 10 &
[1] 850
[1]+ Done    sleep 10

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

Пример.: Запуск цикла в фоновом режиме

#!/bin/bash
# background-loop.sh
for i in 1 2 3 4 5 6 7 8 9 10 # Первый цикл.
do
 echo -n "$i "
done & # Запуск цикла в фоне.
       # Иногда возможны случаи выполнения этого цикла после второго цикла.
echo # Этот 'echo' иногда не отображается на экране.
for i in 11 12 13 14 15 16 17 18 19 20 # Второй цикл.
do
 echo -n "$i "
done
echo # Этот 'echo' иногда не отображается на экране.
# ======================================================
# Ожидается, что данный сценарий выведет следующую последовательность:
# 1 2 3 4 5 6 7 8 9 10
# 11 12 13 14 15 16 17 18 19 20
# Иногда возможен такой вариант:
# 11 12 13 14 15 16 17 18 19 20
# 1 2 3 4 5 6 7 8 9 10 bozo $
# (Второй 'echo' не был выполнен. Почему?)
# Изредка возможен такой вариант:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# (Первый 'echo' не был выполнен. Почему?)
# Крайне редко встречается и такое:
# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20
# Второй цикл начал исполняться раньше первого.
exit 0

Команда, исполняемая в пределах сценария в фоне, может подвесить сценарий, ожидая нажатия клавиши. К счастью, это легко "лечится".

  • && логическая операция AND (логическое И).

В операциях проверки условий, оператор && возвращает 0 (success) тогда, и только тогда, когда оба операнда имеют значение true(ИСТИНА).

  • - Знак дефиса (-)

Префикс ключа. С этого символа начинаются опциональные ключи команд:

COMMAND -[Option1][Option2][...]
ls -al
sort -dfu $filename
set -- $variable
 
if [ $file1 -ot $file2 ];then
 echo "Файл $file1 был создан раньше чем $file2."
fi
if [ "$a" -eq "$b" ]; then
 echo "$a равно $b."
fi
if [ "$c" -eq 24 -a "$d" -eq 47 ]; then
 echo "$c равно 24, а $d равно 47."
fi

- — перенаправление из/в stdin или stdout.

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Перемещение полного дерева файлов и подкаталогов из одной директории в другую
# спасибо Алану Коксу (Alan Cox) <a.cox@swansea.ac.uk>, за небольшие поправки
# 1) cd /source/directory Переход в исходный каталог, содержимое которого будет перемещено
# 2) && "И-список": благодаря этому все последующие команды будут выполнены
# только тогда, когда 'cd' завершится успешно
# 3) tar cf - . ключом 'c' архиватор 'tar' создает новый архив,
# ключом 'f' (file) и последующим '-' задается файл архива -- stdout,
# в архив помещается текущий каталог ('.') с вложенными подкаталогами.
# 4) | конвейер с ...
# 5) ( ... ) subshell-ом (дочерним экземпляром командной оболочки)
# 6) cd /dest/directory Переход в каталог назначения.
# 7) && "И-список", см. выше
# 8) tar xpvf - Разархивирование ('x'), с сохранением атрибутов "владельца" и прав доступа ('p') к файлам,
# с выдачей более подробных сообщений на stdout ('v'),
# файл архива -- stdin ('f' с последующим '-').
#
# Примечательно, что 'x' -- это команда, а 'p', 'v' и 'f' -- ключи
# Во как!
# Более элегантный вариант:
# cd source-directory
# tar cf - . | (cd ../target-directory; tar xzf -)
#
#cp -a /source/directory /dest имеет тот же эффект.
 
bunzip2 linux-2.4.3.tar.bz2 | tar xvf -
# --разархивирование tar-файла-- | --затем файл передается утилите "tar"--
# Если у вас утилита "tar" не поддерживает работу с "bunzip2",
# тогда придется выполнять работу в два этапа, с использованием конвейера.
# Целью данного примера является разархивирование тарбола (tar.bz2) с исходными текстами ядра.

Обратите внимание, что в этом контексте - - не самостоятельный оператор Bash, а скорее опция, распознаваемая некоторыми утилитами Unix (такими как tar, cat и т.п.), которые выводят результаты своей работы в stdout.

bash$ echo "whatever" | cat -
whatever

В случае, когда ожидается имя файла, тогда - перенаправляет вывод на stdout (вспомните пример с tar cf) или принимает ввод с stdin.

bash$ file
Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...

Сама по себе команда file без параметров завершается с сообщением об ошибке. Добавим символ - и получим более полезный результат. Это заставит командный интерпретатор ожидать ввода от пользователя.

bash$ file -
abc
standard input:  ASCII text
bash$ file -
#!/bin/bash
standard input:   Bourne-Again shell script text executable

Теперь команда принимает ввод пользователя со stdin и анализирует его. Используя передачу stdout по конвейеру другим командам, можно выполнять довольно эффектные трюки, например вставка строк в начало файла. С помощью команды diff — находить различия между одним файлом и частью другого:

 grep Linux file1 | diff file2 -

И наконец пример использования служебного символа - с командой tar.

Пример.: Резервное архивирование всех файлов, которые были изменены втечение последних суток

#!/bin/bash
# Резервное архивирование (backup) всех файлов в текущем каталоге,
# которые были изменены в течение последних 24 часов
# в тарболл (tarball) (.tar.gz - файл).
BACKUPFILE=backup
archive=${1:-$BACKUPFILE}
# На случай, если имя архива в командной строке не задано,
# т.е. по-умолчанию имя архива -- "backup.tar.gz"
tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Каталог $PWD заархивирован в файл \"$archive.tar.gz\"."
# Stephane Chazelas заметил, что вышеприведенный код будет "падать"
# если будет найдено слишком много файлов
# или если имена файлов будут содержать символы пробела.
# Им предложен альтернативный код:
# -------------------------------------------------------------------
# find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
# используется версия GNU утилиты "find".
# find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
# более универсальный вариант, хотя и более медленный,
# зато может использоваться в других версиях UNIX.
# -------------------------------------------------------------------
exit 0


Могут возникнуть конфликтные ситуации между опреатором перенаправления - и именами файлов, начинающимися с символа "-". Поэтому сценарий должен проверять имена файлов и предварять их префиксом пути, например, ./-FILENAME, $PWD/-FILENAME или $PATHNAME/-FILENAME. Если значение переменной начинается с символа "-", то это тоже может быть причиной появления ошибок.

var="-n"
echo $var
# В данном случае команда приобретет вид "echo -n" и ничего не выведет.

- — предыдущий рабочий каталог. Команда cd - выполнит переход в предыдущий рабочий каталог, путь к которому хранится в переменной окружения $OLDPWD. Не путайте оператор "-" (предыдущего рабочего каталога) с оператором - (переназначения). Ещё раз напомню, что интерпретация символа "-" зависит от контекста, в котором он употребляется. - — Знак минус в арифметических операциях.

  • = Символ равно (=).

Символ равно (=) выступает в качестве оператора присваивания.

a=28
echo $a # 28

В зависимости от контекста применения, символ = может выступать в качестве оператора сравнения.

  • + Символ плюс (+).

Символ плюс (+) выступает в качестве оператора сложения в арифметических операциях. В зависимости от контекста применения, символ + может выступать как оператор регулярного выражения. + - Ключ (опция). Дополнительный флаг для ключей (опций) команд. Отдельные внешние и встроенные команды используют символ "+" для разрешения некоторой опции, а символ "-" — для запрещения.

  • % Символ процента (%).

Модуль (остаток от деления) — арифметическая операция. В зависимости от контекста применения, символ % может выступать в качестве шаблона.

  • ~ Символ тильда (~).

Соответствует содержимому внутренней переменной $HOME. ~bozo — домашний каталог пользователя bozo, а команда ls ~bozo выведет содержимое его домашнего каталога. ~/ — это домашний каталог текущего пользователя, а команда ls ~/ выведет содержимое домашнего каталога текущего пользователя.

bash$ echo ~bozo
/home/bozo
bash$ echo ~/home/bozo
bash$ echo ~/
/home/bozo/
bash$ echo ~:
/home/bozo:
bash$ echo ~nonexistent-user
~nonexistent-user

~+ — текущий рабочий каталог. Соответствует содержимому внутренней переменной $PWD.
~- — предыдущий рабочий каталог. Соответствует содержимому внутренней переменной $OLDPWD.

  • ^ Символ карет (^).

Начало-строки. В регулярных выражениях символ "^" задает начало строки текста. Управляющий символ изменяет поведение терминала или управляет выводом текста. Управляющий символ набирается с клавиатуры как комбинация CONTROL + <клавиша>.

  1. Ctrl-B — Курсор — на одну позицию назад (без стирания символа).
  2. Ctrl-C — Прерывание выполнения процесса.
  3. Ctrl-D — Выход из командного интерпретатора (log out) (аналог команды exit)."EOF" (признак конца файла). Этот символ может выступать в качестве завершающего привводе с stdin.
  4. Ctrl-G — "BEL" (звуковой сигнал -- "звонок").
  5. Ctrl-H — Backspace ("забой") — удаление предыдущего символа.
  6. Ctrl-I — Горизонтальная табуляция.
  7. Ctrl-J — Перевод строки.
  8. Ctrl-K — Вертикальная табуляция.
  9. Ctrl-L — Перевод формата (очистка экрана (окна) терминала). Аналогична команде clear.
  10. Ctrl-M — Возврат каретки.
  11. Ctrl-Q — Resume (XON). Эта комбинация "восстанавливает" работу stdin терминала.
  12. Ctrl-S — Suspend (XOFF). Эта комбинация "замораживает" stdin терминала. (Для восстановления "работоспособности" — используйте комбинацию Ctrl-Q)
  13. Ctrl-U — Стирание строки ввода.
  14. Ctrl-Z — Приостановка процесса.
#!/bin/bash
# Вставка символа Ctl-H в строку.
a="^H^H" # Два символа Ctl-H (backspace).
echo "abcdef"
# abcdef
echo -n "abcdef$a " 
# abcd f
# Пробел в конце ^ ^ двойной шаг назад.
echo -n "abcdef$a" # abcdef
# Пробела в конце нет, backspace не работает (почему?).
# Результаты могут получиться совсем не те, что вы ожидаете.
echo; echo
 #!/bin/bash
 # Спасибо Lee Maschmeyer, за этот пример.
 read -n 1 -s -p $'Control-M — переводит курсор в начало этой же строки. Нажмите клавишу Enter. \x0d'
 # Разумеется, что, 'x0d' — это # шестнадцатиричный эквивалент  '''''Ctrl-M'''''.
 echo >&2 # Перевод строки 
 read -n 1 -s -p $'Control-J — переводит курсор в начало другой строки. \x0a'
 echo >&2 # Control-J — это перевод строки.
 read -n 1 -s -p $'А Control-K\x0b — вниз.'
 echo >&2 # Control-K — это вертикальная табуляция.
 exit 0
  • Символ пробела ( ).

Пробельный символ используется как разделитель команд или переменных. В качестве пробельного символамогут выступать — собственно пробел (space), символ табуляции, символ перевода строки, символ возврата каретки или комбинация из вышеперечисленных символов. В некоторых случаях, таких как присваивание значений переменным, использование пробельных символов недопустимо. Пустые строки никак не обрабатываются командным интерпретатором и могут свободно использоваться для визуального выделения отдельных блоков сценария. $IFS — переменная специального назначения. Содержит символы-разделители полей, используемые некоторыми командами. По-умолчанию — пробельные символы.

Переменные и параметры. Введение.

Переменные — это одна из основ любого языка программирования. Они участвуют в арифметических операциях, в синтаксическом анализе строк и совершенно необходимы для абстрагирования каких-либо величин с помощью символических имен. Физически переменные представляют собой ни что иное как участки памяти, в которые записана некоторая информация.

Подстановка переменных

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

  • $


Необходимо всегда помнить о различиях между именем переменной и ее значением. Если variable1 — это имя переменной, то $variable1 — это ссылка на ее значение. "Чистые" имена переменных, без префикса $, могут использоваться только при объявлении переменной, при присваивании переменной некоторого значения, при удалении (сбросе), при экспорте и в особых случаях — когда переменная представляет собой название сигнала. Присваивание может производится с помощью символа = (например: var1=27), инструкцией read и в заголовке цикла (for var2 in 1 2 3).
Заключение ссылки на переменную в двойные кавычки (" ") никак не сказывается на работе механизма подстановки. Этот случай называется "частичные кавычки", иногда можно встретить название "нестрогие кавычки". Одиночные кавычки (' ') заставляют интерпретатор воспринимать ссылку на переменную как простой набор символов, потому в одинарных кавычках операции подстановки не производятся. Этот случай называется "полные", или "строгие" кавычки.
Примечательно, что написание $variable фактически является упрощенной формой написания ${variable}. Более строгая форма записи ${variable} может с успехом использоваться в тех случаях, когда применение упрощенной формы записи порождает сообщения о синтаксических ошибках.

Пример: Присваивание значений переменным и подстановка значений переменных.

#!/bin/bash
# Присваивание значений переменным и подстановка значений переменных
a=375
hello=$a
#-------------------------------------------------------------------------
# Использование пробельных символов
# с обеих сторон символа "=" присваивания недопустимо.
# Если записать "VARIABLE =value",
# то интерпретатор попытается выполнить команду "VARIABLE" с параметром "=value".
# Если записать "VARIABLE= value",
# то интерпретатор попытается установить переменную окружения "VARIABLE" в ""
# и выполнить команду "value".
#-------------------------------------------------------------------------
echo hello # Это не ссылка на переменную, выведет строку "hello".
echo $hello
echo ${hello} # Идентично предыдущей строке.
echo "$hello"
echo "${hello}"
echo
hello="A B  C  D"
echo $hello 
# A B C D
echo "$hello" # A B C D
# Здесь вы сможете наблюдать различия в выводе echo $hello и echo "$hello".
# Заключение ссылки на переменную в кавычки сохраняет пробельные символы.
echo
echo '$hello' # $hello
# Внутри одинарных кавычек не производится подстановка значений переменных,
# т.е. "$" интерпретируется как простой символ.
# Обратите внимание на различия, существующие между этими типами кавычек.
hello= # Запись пустого значения в переменную.
echo "\$hello (пустое значение) = $hello"
# Обратите внимание: запись пустого значения — это не то же самое,
# что сброс переменной, хотя конечный результат — тот же.
# --------------------------------------------------------------
# Допускается присваивание нескольких переменных в одной строке,
# если они отделены пробельными символами.
# Внимание! Это может снизить читабельность сценария и оказаться непереносимым.
var1=variable1 var2=variable2 var3=variable3
echo
echo "var1=$var1 var2=$var2 var3=$var3"
# Могут возникнуть проблемы с устаревшими версиями "sh".
# --------------------------------------------------------------
echo; echo
numbers="один два три"
other_numbers="1 2 3"
# Если в значениях переменных встречаются пробелы,
# то использование кавычек обязательно.
echo "numbers = $numbers"
echo "other_numbers = $other_numbers" # other_numbers = 1 2 3
echo
echo "uninitialized_variable = $uninitialized_variable"
# Неинициализированная переменная содержит "пустое" значение.
uninitialized_variable= # Объявление неинициализированной переменной (то же, что и присваивание пустого значения)
echo "uninitialized_variable = $uninitialized_variable" # Переменная содержит "пустое" значение.
uninitialized_variable=23 # Присваивание.
unset uninitialized_variable # Сброс.
echo "uninitialized_variable = $uninitialized_variable" # Переменная содержит "пустое" значение.
echo
exit 0

Неинициализированная переменная хранит "пустое" значение - не ноль! Использование неинициализированных переменных может приводить к ошибкам разного рода в процессе исполнения. Не смотря на это в арифметических операциях допускается использовать неинициализированные переменные.

echo "$uninitialized" # (пустая строка)
let "uninitialized += 5" # Прибавить 5.
echo "$uninitialized" # 5
# Заключение:
# Неинициализированные переменные не имеют значения, однако
# в арифметических операциях за значение таких переменных принимается число 0.
# Это недокументированная (и возможно непереносимая) возможность.

Присваивание значений переменным

  • =


Оператор присваивания (пробельные символы до и после оператора — недопустимы). Не путайте с операторами сравнения = и -eq! Обратите внимание: символ = может использоваться как в качестве оператора присваивания, так и в качестве оператора сравнения, конкретная интерпретация зависит от контекста применения.

Пример: Простое присваивание.

#!/bin/bash
# Явные переменные
echo
# Когда перед именем переменной не употребляется символ '$'?
# В операциях присваивания.
# Присваивание
a=879
echo "Значение переменной \"a\"$a."
# Присваивание с помощью ключевого слова 'let'
let a=16+5
echo "Значение переменной \"a\" теперь стало равным: $a."
echo
# В заголовке цикла 'for' (своего рода неявное присваивание)
echo -n "Значения переменной \"a\" в цикле: "
for a in 7 8 9 11
do
 echo -n "$a "
done
echo
echo
# При использовании инструкции 'read' (тоже одна из разновидностей присваивания)
echo -n "Введите значение переменной \"a\" "
read a
echo "Значение переменной \"a\" теперь стало равным: $a."
echo
exit 0


Пример: Присваивание значений переменным простое и замаскированное.

#!/bin/bash
a=23 # Простейший случай
echo $a
b=$a
echo $b 
# Теперь немного более сложный вариант (подстановка команд).
a=`echo Hello!` # В переменную 'a' попадает результат работы команды 'echo'
echo $a
# Обратите внимание на восклицательный знак (!) в подстанавливаемой команде
# этот вариант не будет работать при наборе в командной строке,
# поскольку здесь используется механизм "истории команд" BASH
# Однако, в сценариях, механизм истории команд запрещен.
a=`ls -l` # В переменную 'a' записывается результат работы команды 'ls -l'
echo $a # Кавычки отсутствуют, удаляются лишние пробелы и пустые строки.
echo
echo "$a" # Переменная в кавычках, все пробелы и пустые строки сохраняются
exit 0


# Взято из /etc/rc.d/rc.local
R=$(cat /etc/redhat-release)
arch=$(uname -m)

Переменные Bash не имеют типа

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

Пример: Целое число или строка?

#!/bin/bash
# int-or-string.sh: Целое число или строка?
a=2334 # Целое число.
let "a += 1"
echo "a = $a " # a = 2335
echo # Всё ещё целое число.
b=${a/23/BB} # замена "23" на "BB".
# Происходит трансформация числа в строку.
echo "b = $b" 
# b = BB35
declare -i b # Явное указание типа здесь не поможет.
echo "b = $b" # b = BB35
let "b += 1" # BB35 + 1 =
echo "b = $b" # b = 1
echo
c=BB34
echo "c = $c" # c = BB34
d=${c/BB/23} # замена "BB" на "23". 
# Переменная $d становится целочисленной.
echo "d = $d" # d = 2334
let "d += 1" # 2334 + 1 =
echo "d = $d" # d = 2335echo
echo
# А что происходит с "пустыми" переменными?
e=""
echo "e = $e" # e =
let "e += 1" # Арифметические операции допускают использование "пустых" переменных?
echo "e = $e" # e = 1
echo # "Пустая" переменная становится целочисленной.
# А что происходит с необъявленными переменными?
echo "f = $f" # f =
let "f += 1" # Арифметические операции допустимы?
echo "f = $f" # f = 1
echo # Необъявленная переменная трансформируется в целочисленную.
# Переменные Bash не имеют типов.
exit 0


Отсутствие типов — это и благословение и проклятие. С одной стороны — отсутствие типов делает сценарии более гибкими(чтобы повеситься — достаточно иметь веревку!) и облегчает чтение кода. С другой — является источником потенциальных ошибок и поощряет привычку к "неряшливому" программированию. Бремя отслеживания типа той или иной переменной полностью лежит на плечах программиста. Bash не будет делать это за вас!

Специальные типы переменных

  • локальные переменные

Переменные, область видимости которых ограничена блоком кода или телом функции (см. также локальные переменные в функциях)

  • переменные окружения

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

В более общем контексте, каждый процесс имеет некоторое "окружение" (среду исполнения), т.е. набор переменных, к которым процесс может 
обращаться за получением определенной информации. В этом смысле командная оболочка подобна любому другому процессу.

Каждый раз, когда запускается командный интерпретатор, для него создаются переменные, соответствующие переменным окружения. Изменение переменных или добавление новых переменных окружения заставляет оболочку обновить свои переменные, и все дочерние процессы (и команды, исполняемые ею)наследуют это окружение.

Пространство, выделяемое под переменные окружения, ограничено. Создание слишком большого количества переменных 
окружения или одной переменной, которая занимает слишком большое пространство, может привести к возникновению определённых проблем.
bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"
bash$ du
bash: /usr/bin/du: Argument list too long

Если сценарий изменяет переменные окружения, то они должны "экспортироваться", т.е. передаваться окружению, локальному по отношению к сценарию. Эта функция возложена на команду export.

Сценарий может экспортировать переменные только дочернему процессу, т.е.командам и процессам запускаемым из данного сценария. 
Сценарий, запускаемый из командной строки не может экспортировать переменные "наверх" командной оболочке. Дочерний процесс не может 
экспортировать переменные родительскому процессу.
  • позиционные параметры

Аргументы, передаваемые скрипту из командной строки — $0, $1, $2, $3..., где $0 — это название файла сценария, $1 — это первый аргумент, $2 — второй, $3 — третий и так далее. Аргументы, следующие за $9, должны заключаться в фигурные скобки, например: ${10}, ${11}, ${12}.
Специальные переменные $* и $@ содержат все позиционные параметры (аргументы командной строки).

Пример: Позиционные параметры.

#!/bin/bash
# Команда вызова сценария должна содержать по меньшей мере 10 параметров, например
# ./scriptname 1 2 3 4 5 6 7 8 9 10
MINPARAMS=10
echo
echo "Имя файла сценария: \"$0\"."
# Для текущего каталога добавит ./
echo "Имя файла сценария: \"`basename $0`\"."
# Добавит путь к имени файла (см. 'basename')
echo
if [ -n "$1" ] # Проверяемая переменная заключена в кавычки.
then
 echo "Параметр #1: $1" # необходимы кавычки для экранирования символа #
fi
if [ -n "$2" ]
then
 echo "Параметр #2: $2"
fi
if [ -n "$3" ]
then
 echo "Параметр #3: $3"
fi
if [ -n "${10}" ] # Параметры, следующие за $9 должны заключаться в фигурные скобки
then 
 echo "Параметр #10: ${10}"
fi
echo "-----------------------------------"
echo "Все аргументы командной строки: "$*""
if [ $# -lt "$MINPARAMS" ]
then
 echo
echo "Количество аргументов командной строки должно быть не менее $MINPARAMS !"
fi
echo
exit 0

Скобочная нотация позиционных параметров даёт довольно простой способ обращения к последнему аргументу, переданному в сценарий из командной строки. Такой способ подразумевает использование косвенной адресации.

args=$# # Количество переданных аргументов.
lastarg=${!args} # Обратите внимание: lastarg=${!$#} неприменимо.

В сценарии можно предусмотреть различные варианты развития событий, в зависимости отимени сценария. Для этого сценарий должен проанализировать аргумент $0 -- имя файласценария. Это могут быть и имена символических ссылок на файл сценария.

Если сценарий ожидает передачи аргументов в командной строке, то при их отсутствии он получит "пустые" переменные, что может вызвать нежелательный
побочный эффект. Один из способов борьбы с подобными ошибками — добавить дополнительный символ в обеих частях операции присваивания, 
где используются аргументы командной строки.
variable1_=$1_# Это предотвратит появление ошибок, даже при отсутствии входного аргумента.
critical_argument01=$variable1_
# Дополнительные символы всегда можно "убрать" позднее.
# Это может быть сделано примерно так:
variable1=${variable1_/_/} # Побочный эффект возникает только если имя переменной # $variable1_ будет начинаться с символа "_".
# Здесь используется один из вариантов подстановки параметров, обсуждаемых далее.
# Отсутствие шаблона замены приводит к удалению.
# Более простой способ заключается
# в обычной проверке наличия позиционного параметра.
if [ -z $1 ]; then 
exit $POS_PARAMS_MISSING
fi

Пример: wh, whois выяснение имени домена.

#!/bin/bash
# Команда 'whois domain-name' выясняет имя домена на одном из 3 серверов:
# ripe.net, cw.net, radb.net
# Разместите этот скрипт под именем 'wh' в каталоге /usr/local/bin
# Требуемые символические ссылки:
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
# ln -s /usr/local/bin/wh /usr/local/bin/wh-cw
# ln -s /usr/local/bin/wh /usr/local/bin/wh-radb
if [ -z "$1" ]
then
 echo "Порядок использования: `basename $0` [domain-name]"
exit 65
fi
case `basename $0` in
# Проверка имени скрипта и, соответственно, имени сервера
"wh" ) whois $1@whois.ripe.net;;
"wh-ripe") whois $1@whois.ripe.net;;
"wh-radb") whois $1@whois.radb.net;;
"wh-cw" ) whois $1@whois.cw.net;;
* ) echo "Порядок использования: `basename $0` [domain-name]";;
esac
exit 0

Команда shift "сдвигает" позиционные параметры, в результате чего парметры "сдвигаются" на одну позицию влево.
$1 <--- $2, $2 <--- $3, $3 <--- $4, и т.д.
Прежний аргумент $1 теряется, но аргумент $0 (имя файла сценария) остаётся без изменений. Если вашему сценарию передаётся большое количество входных аргументов, то команда shift позволит вам получить доступ к аргументам, с порядковым номером больше 9, без использования {фигурных скобок}.

Пример: Использование команды shift.

#!/bin/bash
# Использование команды 'shift' с целью перебора всех аргументов командной строки.
# Назовите файл с этим сценарием, например "shft",
# и вызовите его с набором аргументов, например:
# ./shft a b c def 23 skidoo
until [ -z "$1" ] # До тех пор пока не будут разобраны все входные аргументы...
do
 echo -n "$1 " 
 shift
done
echo # Дополнительная пустая строка.
exit 0

Команда shift может применяться и к входным аргументам функций.

Кавычки

Завершение и код завершения

Проверка условий

Конструкции проверки условий

Операции проверки файлов

Операции сравнения

Вложенные условные операторы if/then

Проверка степени усвоения материала

Операции и смежные темы

Операторы

Числовые константы

Часть третья. Углублённый материал

К вопросу о переменных

Внутренние переменные

Работа со строками

Подстановка параметров

Объявление переменных: declare и typeset

Косвенные ссылки на переменные

$RANDOM: генерация псевдослучайных целых чисел

Двойные круглые скобки

Циклы и ветвления

Циклы

Вложенные циклы

Управление ходом выполнения цикла

Операторы выбора

Внутренние команды

Команды управления заданиями

Внешние команды, программы и утилиты

Базовые команды

Более сложные команды

Команды для работы с датой и временем

Команды обработки текста

Команды для работы с файлами и архивами

Команды для работы с сетью

Команды управления терминалом

Команды выполнения математических операций

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

Команды системного администрирования

Подстановка команд

Арифметические подстановки

Перенаправление ввода/вывода

С помощью команды exec

Перенаправление для блоков кода

Область применения

Встроенные документы

Часть четвёртая. Материал повышенной сложности

Регулярные выражения

Краткое введение в регулярные выражения

Globbing -- Подстановка имен файлов

Подоболочки или Subshells

Ограниченный режим командной оболочки

Подстановка процессов

Функции

Сложные функции и сложности с функциями

Локальные переменные

Рекурсия без локальных переменных

Псевдонимы

Списки команд

Массивы

Файлы

/dev и /proc

/dev

/proc

/dev/zero и /dev/null

Отладка сценариев

Необязательные параметры(ключи)

Широко распространенные ошибки

Стиль программирования

Неофициальные рекомендации по оформлению сценариев

Разное

Интерактивный и неинтерактивный режим работы

Сценарии-обертки

Операции сравнения: Альтернативные решения

Рекурсия

"Цветные" сценарии

Оптимизация

Разные советы

Проблемы безопасности

Проблемы переносимости

Сценарии командной оболочки под Windows

Bash, версия 2

Замечания и дополнения

От автора

Об авторе

Куда обращаться за помощью

Инструменты, использовавшиеся при создании книги

Аппаратура

Программное обеспечение

Благодарности

Библиография

Дополнительные примеры сценариев

Справочная информация

Маленький учебник по Sed и Awk

Sed

Awk

Коды завершения, имеющие предопределённый смысл

Подробное введение в операции ввода-вывода и перенаправление ввода-вывода

Системные каталоги

Локализация

История команд

Пример файла .bashrc

Преобразование пакетных (*.bat) файлов DOS в сценарии командной оболочки

Упражнения

Анализ сценариев

Создание сценариев

Хронология

Авторские права