Скриптинг на REXX

Автор: VaD_

Источник: Социальная сеть системных администраторов

Публикация: 20 мая 2009

 Иногда компания IBM затевает и успешно реализует очень интересные проекты. Так в середине 70х годов 20го века эти ребята решили сделать простой и мощный скриптовый язык. Необычность была в том, что этот язык должен был одинаково эффективно применяться на самых разнообразных и очень непохожих операционных системах. Проект был реализован (ИМХО весьма успешно) в конце 70х. Язык получил название REXX – REstructured eXtended eXecutor. Произносится как «РЕКС». В переводе с латыни – громогласный. :-) Первая публикация о REXX вышла 18 февраля 1981 г. (https://www.rexxla.org/rexxlang/mfc/share56.txt)

Почему сисадмину надо использовать REXX.

1.REXX — интерпретируемый язык с очень мощными средствами обработки строк. А это то, что необходимо в скриптовых языках для того, чтобы парсить вывод вызываемых команд ОС.

2.REXX сконструирован так, что позволяет очень легковесную, технически эффективную реализацию. А следовательно, не требователен к ресурсам и быстро работает. Конечно, возможности более современных языков вроде Perl или Python гораздо больше. Но за эти возможности приходится платить. Когда-то мне нужно было поставить Gentoo на net-book. Оригинальные стартовые скрипты Gentoo написаны на Python. Время загрузки ОС занимало более пяти минут и раздражало нереально. Когда я переписал стартовые скрипты на REXX, ОС стала загружаться буквально на «два щелчка пальцами».

3.REXX – язык классического процедурного стиля, без модных объектных наворотов. Это делает его легче, проще, быстрее. В тоже время, в скриптинге объектный подход не только не нужен, но ИМХО вреден. Почему – тема отдельная. :-)

4.REXX реализован на огромном количестве платформ. От MS-DOS до операционок IBM-овских мэйнфреймов. Это позволяет администратору использовать свои навыки в программировании на REXX и карманные наработки на самых разных платформах.


Преамбула закончена. Начинаем амбулу. :-)

Существует много различных реализаций REXX. В опен-сорсной тусовке наиболее известна Regina-REXX Марка Хесслинга (Mark Hessling). Эту реализацию я и буду использовать в качестве примера. Её же и рекомендую для использования.

Сначала нам надо установить Regina-REXX. Например:

emerge -av regina-rexx # Gentoo
apt-get install regina-rexx # Debian
cd /usr/ports/lang/rexx-regina # FreeBSD


Подробности установки из тарбола для различных дистрибутивов можно посмотреть здесь – regina.sourceforge.net/buildtips.html

Внимание! Regina устанавливается в /usr/bin, /usr/lib под Linux. В /usr/local/bin, /usr/local/lib под FreeBSD и Mac OS X. Если вы собираетесь использовать REXX в скриптах инициализации ОС, ручками скопируйте (например для Linux)
/usr/bin/regina ? /bin
/usr/bin/rexx ? /bin
/usr/lib/libregina.so.* ? /lib

Исполняемый модуль интерпретатора устанавливается в двух вариантах:
1.rexx – статически слинкованый;
2.regina + libregina.so – с динамической библиотекой.

Очевидно, что первый вариант проще в использовании, второй экономнее и быстрее при интенсивном использовании REXX. :-)

Теперь напишем и выполним нашу первую программу на REXX.

#!/usr/bin/regina
# Our first REXX program
SAY 'Hello World!'
EXIT


Не забываем chmod +x наш_файл и выполняем его.

Рекомендация. Пишите ключевые слова языка большими буквами. Обязательно завершайте текст оператором EXIT. Обязательно завершайте файл пустой строкой. По другому обычно тоже будет работать. Но эти простые правила в будущем могут избавить вас от больших проблем. Почему – читайте в документации про особенности реализации и работы интерпретатора. :-)

Итак, что мы узнали.
1.Комментарии можно оформлять так же, как в bash. Однако, это расширение в конкретной реализации языка. По стандарту языка коментарии пишутся так – /* my comments */. Такой коментарий может занимать несколько строк.
2.Символьные строки записываются в апострофах – 'symbol string' – или в двойных кавычках. Если строка заключена в апострофы, то внутри неё можно использовать кавычки. И наоборот.
3.Опрератор SAY выводит строку символов на /dev/stdout.
4.Программа завершается оператором EXIT.

Теперь чуть усложним программу.

#!/usr/bin/regina
# Our second REXX program
Text='Hello World!'
SAY Text
EXIT


Здесь мы использовали переменную Text. REXX язык со слабой типизацией. Переменные не надо заранее объявлять. Имя переменной должно начинаться с буквы и может содержать буквы и цифры. Обратите внимание, что имена переменных регистро-независимы. Так VAR1, Var1 и var1 это одна и та же переменная.

#!/usr/bin/regina
   VAR1 =1
   Var1 =2
   var1 =3
   SAY VAR1 Var1 var1
EXIT


Переменная инициализируется в момент первого использования. Если переменная впервые используется «на чтение», то её значение равно имени переменной в верхнем регистре. Смотрите следующий пример.

#!/usr/bin/regina
# First use of VAR1 and var2 is read
   SAY VAR1
   SAY var2
EXIT


Содержимое переменной – строка символов. Если эта строка имеет числовое значение, то может быть использована в числовых выражениях.

#!/usr/bin/regina
# Calculation
   Var1 ='12'
   Var2 =18
   SAY Var1 + Var2
   SAY
10*(Var1 + Var2)
EXIT


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

Для работы с текстовыми строками существует два типа операции конкатенации (сцепления). Перечисление переменных через пробел и сцепление операцией || (две вертикальных черты). Попробуйте следующий пример и увидите разницу.

#!/usr/bin/regina
   V1 ='AA'
   V2 ='BB'
   V3 ='CC'
   O1 = V1 V2 V3
   O2
= V1||V2||V3
   SAY
'O1 =' O1
   SAY
'O2 =' O2
EXIT


ЗЫ: Фактически в операторах SAY тоже используется одна из операций конкатенации.

Ну и последнее. На одной строке можно записать несколько операторов через точку с запятой.

#!/usr/bin/regina
   V1 ='AA'; V2 ='BB'; V3 ='CC'
   SAY V1 V2 V3
EXIT


Теперь перейдём к основным операторам языка.

Оператор PARSE с опцией ARG разбирает параметры переданные программе или процедуре (функции). Оператор PARSE с опцией VAR разбирает указанную символьную строку.

Создайте исполняемый файл test с таким содержимым:

#!/usr/bin/regina
   PARSE ARG Param1 Param2 Param3
   SAY
'Param1 =' Param1
   SAY
'Param2 =' Param2
   SAY
'Param3 =' Param3
EXIT


А теперь выполните его следующим образом:
./test a b c
./test a b c d e

Надеюсь, интуитивно понятно по каким правилам происходит разборка строки параметров и присвоение значений переменным. :-)

Теперь аналогично разберём символьную строку

#!/usr/bin/regina
   Stroka='1 2 3 4 5'
   PARSE VAR Stroka Param1 Param2 Param3
   SAY
'Param1 =' Param1
   SAY
'Param2 =' Param2
   SAY
'Param3 =' Param3
EXIT


В общем, пока всё очень похоже на read в bash. Однако, оператор PARSE позволяет и значительно более сложные разборки. Например:

#!/usr/bin/regina
   Phone='(916) 123-4567'
   PARSE VAR Phone2Area57Number
   SAY 'Код города:'Area
   SAY 'Телефон:'Number
EXIT


Подробнее возможности оператора PARSE можно посмотреть в документации на REXX. :-)

Операторы IF и ELSE. Условные операторы. Имеют следующую форму
IF условие THEN оператор
ELSE оператор
Ничего сложного. Всё очень похоже на другие языки. Пара примеров:

#!/usr/bin/regina
   PARSE ARG P1 P2
   IF P1
> P2 THEN SAY P1
EXIT


#!/usr/bin/regina
   PARSE ARG P1 P2
   IF P1
> P2 THEN SAY P1
   ELSE SAY P2
EXIT


Список допустимых операций сравнения ищем в документации. Из необходимых срочно: <, >, =, <>, >=, <=. :-)

Перейдём к следующему оператору – DO. REXX язык PL-евского стиля. Что не удивительно, учитывая его место рождения. Для языков подобных PL/I характерен функционально нагруженный оператор DO. К примеру, в зависимости от способа использования он заменяет собой операторы for, while и, даже, фигурные скобки языка C, то есть обрамляет блок операторов. Модернизируем предыдущий пример так, чтобы при выполнении любого условия выполнялось несколько операторов.

#!/usr/bin/regina
   PARSE ARG P1 P2
   IF P1
> P2 THEN DO
      SAY P1
'greater then' P2
      DIFF
= P1 - P2
   END
   ELSE DO
      SAY P2
'greater or equel then' P1
      DIFF
= P2 - P1
   END
   SAY 'Difference =' DIFF
EXIT


В приведённом выше примере пара операторов DO, END выполняет ту же роль, что фигурные скобки { } в языке C.

В следующем примере мы используем возможности оператора DO как оператора цикла. То есть оператора задающего возможность повторного исполнения блока операторов.

#!/usr/bin/regina
   PARSE ARG P1 P2
   IF P1
> P2 THEN DO I = P2 TO P1
      SAY
'Step' I
   END
   ELSE DO I = P1 TO P2
      SAY
'Step' I
   END
EXIT


В следующем примере использование оператора DO для организации вложенных циклов по счётчику.

#!/usr/bin/regina
   DO X =0 TO 10 BY 2
      DO Y =9 TO 0 BY -3
         SAY X Y
     
END
   END
EXIT


Обратите внимание на опцию BY, которая задаёт шаг изменения переменной цикла. Во внешнем цикле она увеличивается с шагом 2, во внутреннем уменьшается с шагом 3. Если опция BY не задана, то шаг изменения переменной цикла равен 1.

Бесконечный цикл можно организовать воспользовавшись опцией FOREVER.

#!/usr/bin/regina
# Это будет крутиться пока не нажмёте Ctrl+C
   I =1
   DO FOREVER
      SAY I
      I
= I +1
   END
EXIT


Цикл, выполняющийся пока выполняется заданное условие, задаётся при помощи опции WHILE.

#!/usr/bin/regina
   I =1
   DO WHILE I <1000
      SAY I
      I
= I +1
   END
EXIT


Опция UNTIL задаёт цикл, работающий до тех пор, пока не выполнится условие, заданное в опции.

#!/usr/bin/regina
   I =1
   DO UNTIL I =100
      SAY I
      I
= I +1
   END
EXIT


Различные опции можно комбинировать в одном операторе DO.

#!/usr/bin/regina
   PARSE ARG J
   DO I
=1 TO 10000 WHILE I < J
      SAY I

   END
EXIT


Теперь перейдём к вводу/выводу. Для ввода данных из файла построчно или посимвольно используются следующие функции:
1.LINES('имя_файла') – есть ли еще не прочитанные строки в файле, 0 = нет
2.LINEIN('имя_файла') – считывает строку из файла
3.CHARS('имя_файла') – есть ли еще не прочитанные символы в файле, 0 = нет
4.CHARIN('имя_файла') – считывает символ из файла

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

#!/usr/bin/regina
   PARSE ARG FileName
   DO WHILE LINES(FileName)<>0
      SAY LINEIN(FileName)
   END
EXIT


Ещё один полезный оператор – INTERPRET. Любая строка символов, переданная ему как параметр, выполняется как оператор языка REXX.

#!/usr/bin/regina
   Message='Ops!'
   Command='SAY'Message
   INTERPRET Command
EXIT


На этот момент мы знаем уже достаточно, чтобы писать на REXX разнообразные программы. Например напишем вполне функциональный калькулятор. :-)

#!/usr/bin/regina
# Easy calculator
   XX ="X="; X0 ="X=X"
   SAY 'Введите арифметическое выражение'
   SAY 'или Q для выхода из программы'
   DO FOREVER
      PULL V
      IF V
='Q' THEN LEAVE
      S
=LEFT(V,1)
      IF(DATATYPE(S,'A')) THEN DO
         IF
(POS('=',V)=0) THEN DO
            INTERPRET XX V
            SAY
'X=' X
        
END
         ELSE INTERPRET V
     
END
      ELSE DO
         INTERPRET X0 V
         SAY
'X=' X
     
END
   END
EXIT


В тексте встречаются некоторые незнакомые операторы, вроде LEAVE, и встроенные функции. Оператор LEAVE завершает исполнение текущего цикла. Что касается встроенных функций, то их много, и их перечисление не является целью данного текста. За этим лучше обратиться к документации по Regina-REXX. Предлагаю в качестве домашнего задания разобраться как работает приведенный выше алгоритм. :-) Ещё отмечу, что это очень хороший пример мощности и выразительности REXX как языка программирования. Для сравнения, попробуйте сделать программу с таким же функционалом например на C. :-)

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

#!/usr/bin/regina
  'ls -l'
EXIT


Или чуть сложнее:

#!/usr/bin/regina
   Cmd='ls -l'
   Cmd
EXIT


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

Но для того, чтобы скриптовый язык был действительно эффективным, надо не только уметь формировать команду, но и получать и обрабатывать её результаты. У REXX и с этим «всё в порядке». Для того, чтобы обсудить эти механизмы сначала рассмотрим ещё один тип данных, уникальный для REXX. Это так называемые «стэмы».

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

Var1 – обычная переменная.
Var1. – стэм.
Var1.1 – первый элемент стэма.
Var1.2 – второй элемент стэма.
И так далее. Важно, что мы можем обратиться к I-тому элементу стэма вот так
Var1.I

Для примера поработаем со стэмом.

#!/usr/bin/regina
   Var.1='A'
   Var.2='B'
   Var.3='C'
   DO I =1 TO 3
     SAY Var.I
   END


Обратите внимание. Оператор присваивания Var. = задаёт для стэма значение по умолчанию.

#!/usr/bin/regina
   Var.='Undefined'
   Var.1='A'
   Var.3='B'
   Var.5='C'
   DO I =1 TO 5
      SAY Var.I
   END
EXIT


Пример посложнее:

#!/usr/bin/regina
# Запомним содержимое файла /etc/passwd в стэм Passwd.
# Вряд ли в этом файле больше 100000 строк
   DO I =1 TO 100000 WHILE LINES('/etc/passwd')<>0
      Passwd.I = LINEIN('/etc/passwd')
   END
# В элементе Passwd.0 запомним число считаных строк. Пригодиццо :)
   Passwd.0= I -1
# Выведем все строки из стэма на консоль.
   DO J =1 TO Passwd.0
     SAY Passwd.J
   END
EXIT


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

#!/usr/bin/regina
   A ='suxx'; OS.A ='Windows'
   A ='rules'; OS.A ='Linux'
# :-)
   A ='suxx'; SAY OS.A A
   A
='rules'; SAY OS.A A
# :-)
EXIT


Итак, стэмы похожи на традиционные массивы с тремя важными отличиями:
1.Элементы стэма могут иметь разные типы. Это могут быть и строки, и числа одновременно.
2.Элементы стэма не имеющие значения не занимают места в памяти.
3.На элементы стэма можно ссылаться не только числовыми индексами.

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

Управляет процессом передачи команд и получения результатов их выполнения оператор ADDRESS. Начнём с примера. Создадим исполняемый файл test следующего содержания:

#!/usr/bin/regina
ADDRESS SYSTEM WITH OUTPUT STEM SOUT. ERROR STEM SERR.
   PARSE ARG Cmd
   IF LENGTH(Cmd)=0 THEN DO
      SAY
'Обязательно введите команду в качестве параметра'
      EXIT
   END
Cmd
   IF SOUT.0>0 THEN DO I =1 TO SOUT.0
      SAY SOUT.I
   END
   IF SERR.0>0 THEN DO
      SAY
'Ошибочка!'
      DO I =1 TO SERR.0
         SAY SERR.I
     
END
   END
EXIT


Обсудим этот пример строка за строкой. В первой стороке в операторе ADDRESS мы сообщаем интерпретатору REXX как мы хотим передавать команды операционной системе и как мы хотим получать результаты работы этих команд.

Опция SYSTEM говорит, что команды должны передаваться через шелл пользователя. Вообще возможны следующие варианты:
1.SYSTEM – Через шелл пользователя. Какой шелл? Наберите echo $SHELL. :-)
2.CMD – Через системную функцию exec().
3.Ещё некоторое количество вариантов. Читайте документацию Regina-REXX :-)

Здесь важно отметить, что оператор ADDRESS компенсирует практически все отличия различных операционных систем. Таким образом набор и значение обсуждаемых опций может отличаться в различных ОС. С другой стороны, в *nix и Windows он совпадает, а именно это и интересует нас в силу чисто исторических причин. :-)

Следующий параметр оператора ключевое слово «WITH» говорит что мы собираемся определить способ обработки вывода команд операционной системы.

OUTPUT STEM SOUT. означает что вывод команды на /dev/stdout надо прехватить и поместить в стэм с именем SOUT.

ERROR STEM SERR. означает что вывод команды на /dev/stderr надо прехватить и поместить в стэм с именем SERR.

Во второй строке нашего скрипта мы все переданные скрипту параметры помещаем в переменную Cmd. В следующем операторе IF проверяем длину строки параметров. Если длина равна нулю, то строка пустая, команда не задана Завершаем работу скрипта.

Далее передаём команду на выполнение.

Перехват вывода команды осуществляется следующим образом. Первая строка, если есть, помещается в первый элемент стэма, вторая во второй и так далее. В нулевой элемент стэма помещается общее количество строк. Если команда ничего не вывела в соответствующий файл, то нулевой элемент стэма будет равен нулю. Нуль строк.

Таким образом, если команда была введена правильно, то SOUT.0 > 0 и мы выводим на консоль содержимое стэма SOUT. Если были ошибки, был вывод на stderr, то SERR.0 > 0 и мы выводим на консоль текст «Ошибочка!» и сообщения об ошибках, хранящиеся в стэме SERR.

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

./test ls -l # корректная команда
./test ls --bbb # команда с ошибкой, неверный флаг
./test ls -l jopa* # корректная команда, но нет таких файлов

Обратите внимание, что в случае ошибки исполнения команды REXX добавляет в вывод строки:
8 *-* Cmd ? Номер строки и инструкция, которая выполнилась с ошибкой
+++ RC=2 +++ ? Код возврата неправильно выполнившейся команды

В общем случае эта информация помогает при написании и отладке скрипта. Однако, когда скрипт готов, это мешает сделать аккуратный красивый вывод на консоль. Чтобы отключить вывод сообщений самого интерпретатора добавьте в самое начало скрипта оператор TRACE с параметром O (Off). Вот так:

#!/usr/bin/regina
TRACE O


Вообще оператор TRACE управляет выводом служебных сообщений интерпретатора и так называемой трассировкой программы. Попробуйте поместить в начало скрипта
TRACE A и повторите экперименты с нашим скриптом. :-) В этом случае мы увидим пошаговый протокол выполнения скрипта. Если задать TRACE R, то в протоколе будут выводится значения переменных в момент исполнения операторов скрипта. TRACE N восстанавливает режим вывода сообщений по умолчанию. Не обязательно трассировать весь скрипт. Можно выбрать только интересующий нас кусок кода. Например:

#!/usr/bin/regina
TRACE O
ADDRESS SYSTEM WITH OUTPUT STEM SOUT
. ERROR STEM SERR.
   PARSE ARG Cmd
   IF LENGTH(Cmd)=0 THEN DO
      SAY
'Обязательно введите команду в качестве параметра'
      EXIT
   END
Cmd
   IF SOUT.0>0 THEN DO I =1 TO SOUT.0
      SAY SOUT.I
   END
   IF SERR.0>0 THEN DO
     
/* включим вывод сообщений о содержимом переменных */
      TRACE R
      SAY
'Ошибочка!'
      DO I =1 TO SERR.0
         SAY SERR.I
     
END
      /* снова выключим */
      TRACE O
   END
EXIT


Вернёмся к оператору ADDRESS. Уберите операторы TRACE и выполните наш скрипт так

./test ps

Теперь замените в операторе ADDRESS опцию SYSTEM на CMD и ещё раз

./test ps

Помедитируем над результатом. :-) Практическая разница для нас заключается в том, что в случае SYSTEM, как уже говорилось, команды передаются операционной системе через шелл. При этом запущеная копия шела гарантировано наследует все необходимые переменные окружения. В случае CMD команда передаётся через системный вызов exec(), что гораздо быстрее, но при этом мы сами должны следить за корректностью переменных окружения. Когда мы используем REXX из обычной пользовательской сессии эта разница не существенна. Однако, если REXX используется в скриптах инициализации системы мы должны сами позаботиться о необходимых переменных окружения.

Для работы с переменными окружения в REXX используется встроенная функция VALUE.

#!/usr/bin/regina
   Path= VALUE('PATH',,'SYSTEM')
   SAY Path
EXIT


Здесь мы запрашиваем значение переменной окружения PATH и запоминаем его в переменной Path. Обратите внимание, что слово PATH заключено в апострофы. Это имя переменной окружения. Также обратите внимание на две запятые подряд. Второй параметр функции VALUE здесь пропущен. Третий параметр определяет пул переменных, с которым мы работаем. SYSTEM означает, что это переменные окружения. Надеюсь, никто не забыл, что такое переменная окружения PATH. :-)

Теперь добавим к нашей переменной PATH ещё одну директорию – $HOME/local/bin.

#!/usr/bin/regina
   Path= VALUE('PATH',,'SYSTEM')
   Home= VALUE('HOME',,'SYSTEM')
   NewPath=Path||':'||Home||'/local/bin'
   Path= VALUE('PATH',NewPath,'SYSTEM')# второй параметр задает новое значение
   SAY Path
EXIT


Теперь, при передаче команды операционной системе, поиск команды будет выполняться так же и в директории $HOME/local/bin.

Вернёмся к оператору ADDRESS. Мы уже знаем как перехватить вывод команды и её сообщения об ошибках. Однако REXX позволяет также сформировать набор строк и передать его на стандартный ввод /dev/stdin вызываемой команды. Аналогично stdout и stderr это делается в операторе ADDRESS при помощи опции INPUT.

ADDRESS SYSTEM WITH OUTPUT STEM SOUT. INPUT STEM SINP.

– при вызове внешней команды содержимое стэма SINP будет передано на стандартный ввод команды, а её вывод перехвачен и помещён в стэм SOUT.

В качестве примера возьмём вполне реальную задачу. Взять список пользователей из файла /etc/passwd и создать список почтовых ящиков в базе данных mysql. В базу пишем логин, хеш пароля и дату создания записи. На одном из серверов у меня более 300 юзеров. Делать руками подобную работу влом. Воспользуемся REXX.

#!/usr/bin/regina
TRACE O
/*
У меня uid-ы нормальных юзеров начинаются с 1000
Всяким ftp, mysql и прочим почтовые ящики не нужны
По-этому просто взять логины и хеши из shadow нельзя

Запомним логины и uid-ы из /etc/passwd в стемах UserName и
UserId так как нам придётся просматривать их не один раз
*/

   I =0
   DO WHILE LINES('/etc/passwd')<>0
      String= LINEIN('/etc/passwd')
      I = I +1
      PARSE VAR StringUserName.I ':'.':'UserId.I ':'.
   END
   J =0
   DO WHILE LINES('/etc/shadow')<>0
      String= LINEIN('/etc/shadow')
      PARSE VAR StringUserName':'Password':'.
      DO I =1 TO 10000 UNTIL UserName.I =UserName
      END
      IF UserId.I <1000|Password='*' THEN ITERATE
      J
= J +1
      SINP.J ='INSERT INTO mailbox VALUES ("'||UserName||'","'||Password||'",NOW());'
   END
/*
стэм SINP теперь содержит список операторов mysql
поместим в элемент SINP.0 число строк, определим
стэм как источник для /dev/stdin и вызовем mysql
*/

   SINP.0= J
   ADDRESS CMD WITH INPUT STEM SINP
.
   'mysql -D email'
EXIT


В тексте встретился новый оператор – ITERATE. Этот оператор завершает исполнение текущего шага цикла. Операторы после него выполнены не будут. Нам не нужно генерить операторы SQL для записей с UID < 1000 или с залоченным паролем. Цикл перейдёт на следующий шаг. Вертикальная черта в условии в операторе IF означает «или».

Формат одного из операторов PARSE разберем подробнее

PARSE

VAR String ? разбираем переменную String

Оставшаяся часть оператора называется шаблоном (template) разбора. Обратите внимание, что все элементы шаблона должны разделяться пробелами.

UserName.I ':' ? от начала строки до ':' поместить в I-ый элемент стэма UserName
. ':' ? точка означает «пропустить», пропустить до следующего двоеточия
UserId.I ':' ? эту часть до ':' поместить в I-ый элемент стэма UserId
. ? точка, пропустить всё остальное до конца строки

И ещё. Обратите внимание, что в этом скрипте мы используем переменную UserName и стэм UserName. Вообще говоря, имена на самом деле не совпадают :-) Помним, что имя стэма заканчивается точкой.

Вот собственно и всё. В конце статьи мы оказались в состоянии выполнить вполне практическую задачу. :-)

Полезные ссылки:
regina-rexx.sourceforge.net/
google: REXX

Благодарности и порицания.

Выносится порицание Сан Санычу (aka Tank) за то, что он втянул автора в авантюру с написанием данной статьи. :-)

Выносится благодарность Сан Санычу (aka Tank) за то, что он помог автору превратить поток сознания в связный, читаемый текст. :-)