Linux Tools: shells, ash #2 - ash syntax, simple commands

linux linux-tools shell ash

В прошлой статье поговорили про запуск ash, в этой начнем разбираться с самым интересным - синтаксис и написание shell-скриптов. Для изучения продолжаем использовать ash из busybox.

Предыдущая статья: shells, ash #1 - ash startup and arguments

Следующая статья: shells, ash #3 - ash syntax, redirections

Тут нужно сделать небольшое отступление про стандарты. Существует стандарт POSIX 1003.2, который определяет много чего, но сейчас нам важно что он определяет как должна работать командная оболочка, какие команды должны существовать, как работать.

Так вот ash практически ее поддерживает судя по документации:

... is in the process of being changed to conform
with the POSIX 1003.2 and 1003.2a specifications for the shell.

...

Only features designated by POSIX, plus a few Berkeley extensions,
are being incorporated into this shell.

...

Видимо полная реализация стандарта довольно трудоемкое (и противоречивое) занятие, поэтому даже этот шелл имеет какие-то свои нестандартные фичи. К слову у bash их еще больше и называют их bashisms, но в нем есть режим совместимости с POSIX.

Возвращаемся к ash и сегодня поговорим про то как можно писать команды и какой синтаксис и удобства предоставляет нам шелл.

ash читает скрипт построчно, разбивает его на слова по пробелам и табам. Дальше некоторые последовательности символов определяются как операторы. Операторы бывают двух видов:

  1. Control operators — для объединения команд в группы, цепочки-пайпланы и условия
& && ( ) ; ;; | || <newline>
  1. Redirection operators — для перенаправления потоков между командами и не только
< > >| << >> <& >& <<- <>

Стандартной проблемой в шеллах и в языках программирования является использование специальных символов (и последовательностей) как обычных символов – это называют экранированием (escape special chars). В ash экранирование спец символов можно сделать 3 способами:

обратный слэш — backslash

Backslash перед спец символом делает его обычным символом

# команда ничего не выведет,
# потому что точка с запятой интеретируется как разделитель команд
$ echo ;

# выведется точка с запятой, как обычный символ
$ echo \;
;

Кроме этого backslash перед символом конца строки (переводом строки) интерпретируется как продолжение строки

$ echo 1\
2\
3
123

парные одинарные кавычки — matched single quotes

Все что внутри одинарных кавычек, кроме одинарных кавычек, считается обычным символом

$ echo '&;|>>'
&;|>>

Одинарные кавычки при необходимости можно вывести отдельно с помощью backslash

# последняя кавычка экранирована с помощью backslash
# и считается обычным символом
$ echo '&|$'\'
&|$'

$ echo '$PATH'
$PATH

парные двойные кавычки — matched double qoutes

С двойными кавычками все немного сложнеe. Все символы внутри двойных кавычек считаются обычными, за исключением символов знака доллара (dollarsign) - $, обратной кавычки (backquote) - ` и обратного слэша (backslash) - \

backslash внутри двойных кавычек позволяет экранировать только символы: $ ` " \ <newline>

Кратко рассмотрим зачем вообще нужны эти символы – дальше они будут разобраны подробнее.

$ - dollarsign

с помощью $ можно использовать shell-переменные вообще без кавычек

$ echo $PATH
/sbin:/usr/sbin:/bin:/usr/bin

# аналогично $ работает в двойных кавычках
$ echo "$PATH"
/sbin:/usr/sbin:/bin:/usr/bin

` - backquote

команда, заключенная в обратные кавычки, выполняется и output команды подставляется вместо обратных кавычек. Работает и без двойных кавычек.

$ echo `pwd`
/home/ubuntu

$ echo "current directory is `pwd`"
current directory is /home/ubuntu

\ - backslash

работает как и раньше, экранирует символы: $ ` " \ и backslash перед переводом строки считается продолжением строки без переноса

$ echo "\$PWD = \
\"$PWD\""
$PWD = "/home/ubuntu"

Комбинирование разных кавычек для команд и аргументов

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

$ echo my' path is '" = "$PWD""
my path is  = /home/ubuntu

Главное чтобы в рамках одного аргумента строки соединялись без пробелов, иначе после пробела shell будет считать строку уже другим аргументом.

Команды

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

Если при обработке строки скрипта первое слово не является зарезервирванным, то ash начинает обработку этой строки как команды.

Команды могут быть простыми — вызов одной команды, или сложными конструкциями из нескольких связанных команд с условиями или без. Начнем по порядку с простых команд.

Для примера рассмотрим команду env, которая выводит текущие переменные окружения (environment variables).

$ env
SHLVL=1
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/home/ubuntu

Команда вывела несколько переменных окружнения:

Несколько слов про переменные - variables

Команда запускает процесс, у процесса могут быть переменные окружения (environment variables, env vars). Переменные окружения для конкретной команды можно задать непосредственно при вызове:

$ a=1 env
SHLVL=1
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/home/ubuntu
a=1 # <<< переменная 'a' есть в переменных окружения

Вообще и в интерактивном шелле и в скриптах все что соответствует паттертну name=value с начала строки (или после служебного слова) считается переменной и если после переменных следует команда, то все эти переменные попадут в переменные окружения процесса запущенного этой командой:

$ a=1 b=2 env
# ...
a=1
b=2

Если переменные указаны без команды, то они определяются как переменные шелла - shell variables.

Shell variables доступны для подстановки в самом шелле, но во все выполняемые команды передаются только environment variables.

Shell-переменные (в том числе environment-переменные) можно посмотреть builtin-командой set, а env-переменные командой env или export.

Несколько примеров чтобы стало понятнее

# определяем shell-переменную my_test_var
$ my_test_var=1

# смотрим что переменная определилась
# в результатах set выводятся вообще все переменные,
# в том числе environment variables
$ set
# ...
PS4='+ '
PWD='/home/ubuntu'
SHLVL='1'
my_test_var='1' # <<< тут есть переменная 'my_test_var'

# так как my_test_var еще не environment variable,
# ее нет в выводе команды env
$ env
SHLVL=1
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/home/ubuntu

# но ее можно использовать в самом шелле
$ echo 'my_test_var = '$my_test_var
my_test_var = 1

# для того чтобы переменная стала environment-переменной
# ее нужно экспортировать
$ export my_test_var

# теперь переменная my_test_var является environment-переменной
# и передаётся в environment всех команд запущенных в шелле
$ env
SHLVL=1
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/home/ubuntu
my_test_var=1 # <<<

# для конкретного вызова команды
# environment-переменную всегда можно переопределить
$ my_test_var=100500 env
# ...
my_test_var=100500

Пример показывает что все переменные доступны для подстановки внутри shell, но только environment-переменные передаются в окружение всем командам, выполняемым в shell.

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

Для более явного разделения переменных существует негласное правило:

environment-переменные пишутся заглавными буквами, а обычные shell-переменные строчными

# PATH - environment-переменная
$ echo $PATH

# my_test_var - обычная shell-переменная
$ my_test_var=1

 

Итого

Сегодня начали разбирать синтаксис и посмотрели как можно писать команды и аргументы с помощью кавычек, как формируется команда, как работать с переменными в общем и с переменными окружения в частности. В следующей статье разберемся как управлять перенаправлением потоков stdin, stdout и stderr с помощью redirection operators.

Следующая статья: shells, ash #3 - ash syntax, redirections

 

Ссылки


Все статьи серии “Linux Tools”