Основы JCL для начинающих: от теории к практике

Этот курс предназначен для знакомства с языком управления заданиями (JCL) в среде мейнфреймов IBM. Вы изучите базовый синтаксис, ключевые операторы и научитесь создавать рабочие сценарии с разбором реальных примеров.

Введение в JCL: структура, синтаксис и основные правила написания кода

Введение в JCL: структура, синтаксис и основные правила написания кода

Добро пожаловать в курс «Основы JCL для начинающих». Если вы решили погрузиться в мир мейнфреймов (Mainframes) и операционной системы z/OS, то первое, с чем вам предстоит столкнуться — это JCL. Это не просто «еще один язык программирования», это ключ к управлению мощнейшими вычислительными машинами в мире.

В этой первой статье мы разберем фундамент: что такое JCL, зачем он нужен, почему он выглядит именно так (спойлер: виноваты перфокарты) и как написать свой первый корректный код.

Что такое JCL и зачем он нужен?

JCL расшифровывается как Job Control Language (Язык Управления Заданиями). Важно сразу понять главное отличие: JCL — это не язык программирования в привычном смысле, как Python, Java или C++. Вы не пишете на нем алгоритмы, циклы for или сложную бизнес-логику.

JCL — это скриптовый язык, который служит «посредником» между вашей программой и операционной системой мейнфрейма (z/OS). Он говорит системе:

  1. Кто запускает задачу (информация о пользователе).
  2. Какую программу нужно выполнить.
  3. Где находятся данные для этой программы (входные файлы) и куда положить результат (выходные файлы).

Представьте, что мейнфрейм — это огромная кухня ресторана, а операционная система — шеф-повар. Программы — это рецепты. А JCL — это заказной чек (тике́т), который официант передает на кухню. В чеке написано: «Столик №5 (JOB), приготовить стейк (EXEC), используя мясо из холодильника №2 (DD)». Без этого чека кухня не узнает, что ей делать, даже если рецепты идеальны.

Наследие перфокарт: правило 80 колонок

Когда вы впервые видите код JCL, он может показаться архаичным. Причина кроется в истории. JCL создавался в эпоху, когда основным носителем информации были перфокарты. Стандартная перфокарта имела 80 колонок в ширину.

Хотя физические перфокарты давно исчезли, структура JCL в z/OS до сих пор строго привязана к этому формату. Каждая строка кода JCL — это виртуальная перфокарта длиной в 80 символов.

Структура строки JCL

Строка JCL делится на строго определенные поля. Нарушение границ этих полей приведет к ошибке JCL Error, и ваше задание не запустится.

Рассмотрим анатомию строки:

Поле Колонки Описание
Identifier (Идентификатор) 1-2 Почти всегда это два слеша //. Они говорят системе: «Это строка JCL».
Name (Имя) 3-10 Имя задания, шага или файла. Должно начинаться с 3-й колонки.
Operation (Операция) 11+ Команда, которую нужно выполнить (например, JOB, EXEC, DD).
Parameter (Параметры) После операции Аргументы для команды. Отделяются от операции пробелом.
Comments (Комментарии) После параметров Необязательный текст для пояснения кода, отделяется пробелом.
Sequence (Нумерация) 73-80 Исторически использовалось для нумерации карт. Сейчас обычно игнорируется системой, но писать код здесь нельзя.

Три кита JCL: JOB, EXEC, DD

Любое задание (Job) в JCL строится на трех основных операторах. Если вы запомните их, вы поймете 90% логики любого JCL-скрипта.

1. JOB (Задание)

Это «паспорт» вашего задания. Оператор JOB всегда идет первым. Он сообщает системе имя задания, информацию о владельце, бухгалтерские данные и глобальные настройки.

Пример:

//MYJOB01  JOB  (1234),'IVAN IVANOV',CLASS=A,MSGCLASS=X
  • MYJOB01 — имя задания.
  • JOB — операция.
  • Остальное — параметры (номер счета, имя программиста, класс выполнения).

2. EXEC (Выполнение)

Это команда «На старт!». Оператор EXEC (Execute) указывает, какую именно программу или процедуру нужно запустить.

Пример:

//STEP1    EXEC PGM=IEFBR14
  • STEP1 — имя шага (Step name).
  • EXEC — операция.
  • PGM=IEFBR14 — параметр, указывающий имя программы (в данном случае это системная «пустышка», которая ничего не делает, но полезна для тестов).

3. DD (Определение данных)

Это связующее звено. DD (Data Definition) связывает внутреннее имя файла, которое использует программа, с реальным физическим файлом (датасетом) на диске или ленте.

Пример:

//DD1      DD   DSN=MY.DATA.SET,DISP=SHR
  • DD1 — имя, которое ищет программа внутри своего кода.
  • DD — операция.
  • DSN=... — реальное имя файла в системе.

Синтаксис и правила написания

JCL очень чувствителен к синтаксису. Одна лишняя запятая или пробел могут «уронить» задание. Вот свод основных правил, которые нужно выучить наизусть.

1. Идентификатор //

Все исполняемые строки JCL обязаны начинаться с // в колонках 1 и 2. Исключение составляют только данные внутри потока (in-stream data), но об этом мы поговорим в следующих статьях.

2. Имена (Name Field)

  • Имя должно начинаться строго в колонке 3.
  • Длина имени: от 1 до 8 символов.
  • Допустимые символы: A-Z, 0-9 и национальные символы (@, #, $).
  • Первый символ не может быть цифрой.

3. Пробелы имеют значение

В JCL пробел — это разделитель полей.

  • Между именем и операцией должен быть хотя бы один пробел.
  • Между операцией и параметрами должен быть хотя бы один пробел.
  • ВНИМАНИЕ: Если вы случайно поставите пробел внутри списка параметров, система посчитает, что параметры закончились и дальше идет комментарий.

Неправильно:

//STEP1 EXEC PGM=MYPROG, PARM='TEST'

(Здесь пробел после запятой перед PARM приведет к ошибке или игнорированию параметра PARM, так как система сочтет его комментарием).

Правильно:

//STEP1 EXEC PGM=MYPROG,PARM='TEST'

4. Комментарии

Чтобы оставить заметку для себя или коллег, используйте строку, начинающуюся с //* в колонках 1-3.

//* ЭТО СТРОКА КОММЕНТАРИЯ. ОНА НЕ ИСПОЛНЯЕТСЯ.
//MYJOB JOB ...

Также можно писать комментарии в конце исполняемой строки, отделив их пробелом от последнего параметра.

5. Перенос строки (Continuation)

Часто параметры не помещаются в одну строку (помните про ограничение до 71-й колонки, так как 72-80 часто резервируются). Чтобы перенести код на следующую строку:

  1. Завершите текущую строку запятой ,.
  2. На следующей строке начните с // в колонках 1-2.
  3. Оставьте пробелы с 3-й по 15-ю колонку (рекомендуется).
  4. Продолжайте писать параметры с 4-й по 16-ю колонку (обычно начинают с 16-й для аккуратности).

Пример переноса:

//DD1      DD   DSN=VERY.LONG.DATASET.NAME.FOR.EXAMPLE,
//              DISP=(NEW,CATLG,DELETE),
//              UNIT=SYSDA,
//              SPACE=(CYL,(10,5))

Параметры: Позиционные и Ключевые

В поле операндов параметры делятся на два типа:

  1. Позиционные параметры: Должны идти в строгом порядке сразу после операции. Их смысл зависит от их места в строке. Например, в операторе JOB первым часто идет информация о бухгалтерии, а вторым — имя программиста.
  2. Ключевые параметры (Keyword parameters): Могут идти в любом порядке после позиционных. Они всегда имеют вид КЛЮЧ=ЗНАЧЕНИЕ.
    • Примеры: CLASS=A, PGM=IEFBR14, DSN=MY.FILE.

Пример простейшего задания (Hello World)

Давайте соберем всё вместе и напишем валидный JCL код. Поскольку в JCL нет команды `print(

Оператор JOB: идентификация задания и управление параметрами выполнения

Оператор JOB: идентификация задания и управление параметрами выполнения

В предыдущей статье мы заложили фундамент, разобрав структуру строки JCL и «три кита» языка: JOB, EXEC и DD. Мы сравнили JCL с заказным чеком в ресторане, где JOB — это «шапка» чека, определяющая, чей это заказ и кто за него платит.

Сегодня мы детально разберем оператор JOB. Это первая строка любого задания, и без неё система z/OS просто не примет ваш код к исполнению. Мы узнаем, как правильно представиться системе, как управлять приоритетом выполнения и куда направить отчеты о работе.

Анатомия оператора JOB

Оператор JOB выполняет три критически важные функции:

  1. Идентификация (Identification): Присваивает заданию уникальное имя.
  2. Учет (Accounting): Сообщает системе, на какой счет записать затраченные ресурсы (процессорное время, память).
  3. Параметры выполнения (Execution Parameters): Задает глобальные настройки для всего задания (приоритет, лимиты времени, уведомления).

Базовый синтаксис выглядит так:

//ИМЯЗАДАНИЯ JOB (УЧЕТ),'ИМЯ ПРОГРАММИСТА',КЛЮЧЕВЫЕ_ПАРАМЕТРЫ

Имя задания (Job Name)

Как мы помним, имя начинается с 3-й колонки. В корпоративной среде имя задания часто строго регламентировано. Обычно оно состоит из вашего логина (User ID) и одного-двух дополнительных символов.

Важно: Если ваш User ID в системе — USER01, то система безопасности (например, RACF) может разрешить вам запускать только задания, чьи имена начинаются с USER01 (например, USER01A, USER01T). Попытка запустить MYJOB01 приведет к ошибке доступа.

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

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

1. Учетная информация (Accounting Information)

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

Примеры:

  • (123456) — просто номер счета.
  • (1234,DEPT01) — номер счета и отдел.

2. Имя программиста (Programmer Name)

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

Пример:

//MYJOB01 JOB (9999),'IVAN IVANOV'

Ключевые параметры (Keyword Parameters)

После позиционных параметров следуют ключевые. Они записываются в формате КЛЮЧ=ЗНАЧЕНИЕ и могут идти в любом порядке. Именно они дают вам контроль над тем, как будет выполняться задание.

Рассмотрим самые важные из них.

CLASS: Где ждать выполнения?

Параметр CLASS определяет, в какую очередь (Input Queue) попадет ваше задание. Значение — это один символ (A-Z, 0-9).

Представьте аэропорт. Есть стойки регистрации для эконом-класса, бизнес-класса и экипажа. В z/OS системные администраторы настраивают классы похожим образом:

  • CLASS=A: Обычные задания, средний приоритет.
  • CLASS=X: Быстрые тестовые задания, но с ограничением по времени.
  • CLASS=Z: Тяжелые ночные задания, которые запускаются, когда нагрузка на систему минимальна.
//MYJOB01 JOB (9999),'USER',CLASS=A

MSGCLASS: Куда отправить отчет?

Не путайте с CLASS. Параметр MSGCLASS (Message Class) управляет выводом (Output Queue). Когда задание завершится, система сформирует системный лог (JES Log). Куда его деть?

  • MSGCLASS=H (Hold): Положить в очередь удержания. Вы сможете просмотреть его с экрана через утилиту SDSF.
  • MSGCLASS=A: Отправить сразу на физический принтер (да, в некоторых банках это до сих пор используется).
  • MSGCLASS=X: Часто используется как стандартный класс для просмотра с экрана.

MSGLEVEL: Насколько подробным должен быть отчет?

Этот параметр регулирует «болтливость» системы. Он состоит из двух цифр: MSGLEVEL=(S,M).

  1. S (Statements) — нужно ли печатать в лог текст вашего JCL-кода?

    • 0 — печатать только строку JOB.
    • 1 — печатать все строки JCL (включая процедуры).
    • 2 — печатать только сам JCL (без расшифровки процедур).
  2. M (Messages) — нужно ли печатать сообщения о выделении устройств и памяти?

    • 0 — только если задание упало с ошибкой (ABEND).
    • 1 — всегда печатать все сообщения.

Рекомендация: Для отладки и обучения всегда используйте MSGLEVEL=(1,1). Это даст вам полную картину того, что происходило в системе.

NOTIFY: Кто узнает о завершении?

Когда вы запускаете задание, оно уходит «в недра» мейнфрейма. Как узнать, что оно закончилось? Параметр NOTIFY указывает системе отправить сообщение конкретному пользователю.

Обычно используют специальную системную переменную &SYSUID, которая автоматически подставляет ваш логин.

//MYJOB01 JOB (9999),'USER',NOTIFY=&SYSUID

Когда задание завершится, вы увидите всплывающее сообщение: JOB MYJOB01 ENDED - COMP CODE=0000.

TIME: Защита от бесконечных циклов

Параметр TIME ограничивает процессорное время, которое может потратить задание. Это страховка. Если ваша программа попадет в бесконечный цикл, система принудительно остановит её (ABEND S322), когда выйдет время, и не даст «съесть» все ресурсы машины.

Формат: TIME=(минуты,секунды).

Пример:

//MYJOB01 JOB (9999),'USER',TIME=(0,30)

(Ограничение: 30 секунд процессорного времени).

REGION: Сколько памяти нужно?

Параметр REGION запрашивает объем оперативной памяти для шагов задания. Можно указывать в килобайтах (K) или мегабайтах (M).

  • REGION=4M — выделить 4 мегабайта.
  • REGION=0M — выделить всю доступную память (использовать с осторожностью!).

Сводная таблица параметров

Параметр Тип Описание Пример
(Account) Позиционный Номер счета для биллинга (1234)
Name Позиционный Имя программиста 'IVANOV'
CLASS Ключевой Очередь на выполнение (вход) CLASS=A
MSGCLASS Ключевой Очередь вывода логов (выход) MSGCLASS=X
MSGLEVEL Ключевой Детализация логов MSGLEVEL=(1,1)
NOTIFY Ключевой Уведомление пользователя NOTIFY=&SYSUID
TIME Ключевой Лимит CPU времени TIME=1440
REGION Ключевой Лимит памяти REGION=0M

Пример идеального заголовка JOB

Давайте соберем всё вместе. Допустим, мы пишем учебное задание. Нам нужно видеть полный лог, мы хотим получить уведомление о завершении, и нам не нужно много памяти.

//STUDENT1 JOB (EDU100),'LEARNER',
//             CLASS=A,
//             MSGCLASS=X,
//             MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             TIME=(0,10),
//             REGION=4M

Обратите внимание на запятые в конце строк. Это правило переноса, которое мы обсуждали в первой статье. Такой код легко читать и легко поддерживать.

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

  1. Отсутствие пробела после JOB.
    • Ошибка: //MYJOB JOB(123)...
    • Правильно: //MYJOB JOB (123)...
  2. Неверный порядок позиционных параметров. Учетная информация всегда первая, имя программиста — второе. Нельзя написать //JOB 'NAME',(ACCT).
  3. Забытая запятая при переносе. Если вы разбили параметры на несколько строк, но забыли запятую в конце первой строки, система проигнорирует вторую строку или выдаст ошибку.

Заключение

Оператор JOB — это ваша визитная карточка в системе z/OS. Правильная настройка параметров CLASS и MSGCLASS гарантирует, что ваше задание запустится быстро, а результаты вы найдете там, где ожидаете. Использование NOTIFY и MSGLEVEL сэкономит вам часы при отладке.

В следующей статье мы перейдем к «сердцу» задания — оператору EXEC, и узнаем, как запускать программы и передавать им параметры.

Оператор EXEC: запуск программ, процедур и управление условиями выполнения (COND)

Оператор EXEC: запуск программ, процедур и управление условиями выполнения (COND)

Мы продолжаем наше путешествие по миру JCL. В первой статье мы разобрали структуру «перфокарты», во второй — научились заполнять «паспорт» задания с помощью оператора JOB. Теперь, когда система знает, кто мы такие и кто оплачивает банкет, пришло время перейти к главному блюду.

Оператор EXEC (Execute) — это сердце любого шага (Step) в JCL. Именно здесь мы говорим системе: «Запусти вот эту программу» или «Выполни вот этот набор инструкций». Если JOB — это заголовок чека, то EXEC — это конкретные позиции заказа.

В этой статье мы разберем, как запускать программы и процедуры, как передавать им параметры и, самое главное, как управлять логикой выполнения с помощью одного из самых запутанных параметров в JCL — COND.

Анатомия оператора EXEC

Оператор EXEC отмечает начало каждого шага задания (Job Step). В одном задании (Job) может быть до 255 шагов. Каждый шаг выполняется последовательно, один за другим, если не указано иное.

Базовый синтаксис выглядит так:

//ИМЯШАГА EXEC ПАРАМЕТРЫ

Имя шага (Step Name)

Как и в операторе JOB, имя шага начинается с 3-й колонки. Оно не является строго обязательным, но настоятельно рекомендуется его указывать. Без уникального имени шага вы не сможете ссылаться на него в дальнейшем (например, для проверки условий выполнения или перезапуска задания с определенного места).

Что мы запускаем: PGM vs PROC

У оператора EXEC есть два основных способа указать, что именно нужно выполнить. Это первый позиционный параметр.

1. Запуск программы (PGM)

Если вы хотите запустить скомпилированную программу (Load Module) или системную утилиту, вы используете ключевое слово PGM.

//STEP01  EXEC PGM=IEFBR14

В данном примере IEFBR14 — это имя программы. Кстати, IEFBR14 — это знаменитая системная утилита-пустышка. Она не делает абсолютно ничего и сразу завершается успешно. Её часто используют для тестирования синтаксиса JCL или для удаления/создания файлов через параметры DD (о которых мы поговорим в следующей статье).

2. Запуск процедуры (PROC)

Процедура (Procedure или PROC) — это заранее написанный кусок JCL-кода, сохраненный в системной библиотеке. Это аналог функций или макросов в языках программирования. Процедуры позволяют не переписывать одни и те же строки кода сотни раз.

Для запуска процедуры можно использовать ключевое слово PROC, но чаще всего его опускают и просто пишут имя процедуры.

//STEP02  EXEC MYPROC

Или полная форма:

//STEP02  EXEC PROC=MYPROC

Передача данных в программу: Параметр PARM

Иногда программе нужно передать небольшие вводные данные прямо из JCL, не создавая для этого отдельный файл. Для этого используется ключевой параметр PARM.

//STEP03  EXEC PGM=MYPROG,PARM='NEW,DATE=20231025'

Ограничения PARM:

  • Максимальная длина строки — 100 символов.
  • Если в строке есть спецсимволы, её нужно брать в апострофы.

Программа получит эту строку при запуске и сможет разобрать её, чтобы настроить свою логику (например, режим работы «NEW» и дату).

Коды возврата (Return Codes)

Прежде чем переходить к условиям, нужно понять, что такое код возврата. Когда любой шаг завершается, он сообщает системе число — Return Code (RC). Это оценка того, как прошла работа.

Стандартные значения RC в мире мейнфреймов:

Код (RC) Значение Описание
0000 OK Все прошло успешно.
0004 WARNING Предупреждение. Задание выполнено, но были мелкие недочеты.
0008 ERROR Ошибка. Часть задания не выполнена.
0012 SEVERE Серьезная ошибка. Результаты недостоверны.
0016 TERMINAL Фатальная ошибка. Программа не смогла работать.

Эти коды — основа для управления логикой выполнения следующих шагов.

Управление потоком: Параметр COND

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

Главная проблема восприятия COND заключается в его обратной логике.

В обычных языках программирования (IF): «Если условие истинно, то СДЕЛАЙ это». В JCL (COND): «Если условие истинно, то ПРОПУСТИ (не делай) это».

Запомните мантру: «Если True, то Skip» (Если Истина, то Пропусти).

Синтаксис COND

Параметр COND может быть указан как в операторе JOB (действует на все шаги), так и в EXEC (действует на конкретный шаг).

Формат записи:

COND=(код,оператор,имя_шага)
  • Код: Число, с которым мы сравниваем.
  • Оператор: Логическое сравнение.
  • Имя_шага (необязательно): Чей код возврата проверять. Если не указано, проверяются все предыдущие шаги.

Операторы сравнения

В JCL используются буквенные обозначения операторов:

  • GT (Greater Than) — Больше (>>)
  • GE (Greater or Equal) — Больше или равно (≥≥)
  • EQ (Equal) — Равно (==)
  • NE (Not Equal) — Не равно (≠≠)
  • LT (Less Than) — Меньше (<<)
  • LE (Less or Equal) — Меньше или равно (≤≤)

Как читать условие

Формула проверки выглядит так:

Result=C Op RCResult = C \ Op \ RC

Где ResultResult — результат проверки (Истина/Ложь), CC — константа, которую вы написали в JCL, OpOp — оператор сравнения, RCRC — реальный код возврата предыдущего шага.

Если ResultResult — Истина, шаг НЕ выполняется.

Пример 1: Простая логика
//STEP2  EXEC PGM=PROG2,COND=(4,LT)

Читаем: «Если 4 меньше, чем RC предыдущего шага, то пропустить STEP2».

Математически:

4<RC4 < RC

Где 44 — наша константа, << — оператор LT, RCRC — код возврата предыдущего шага.

  • Если предыдущий шаг вернул 0: 4<04 < 0 — Ложь. Шаг выполняется.
  • Если предыдущий шаг вернул 4: 4<44 < 4 — Ложь. Шаг выполняется.
  • Если предыдущий шаг вернул 8: 4<84 < 8 — Истина. Шаг пропускается.

Вывод: Этот шаг запустится, только если предыдущие завершились с кодом 0 или 4 (OK или Warning).

Пример 2: Ссылка на конкретный шаг
//STEP3  EXEC PGM=PROG3,COND=(0,EQ,STEP1)

Читаем: «Если 0 равно RC шага STEP1, то пропустить».

Математически:

0=RCstep10 = RC_{step1}

Где 00 — константа, == — оператор EQ, RCstep1RC_{step1} — код возврата шага STEP1.

Это означает: «Выполнять STEP3 только в том случае, если STEP1 завершился с ошибкой (не 0)». Если STEP1 прошел успешно (0), условие станет Истиной, и STEP3 будет пропущен.

Специальные значения COND

Существуют два особых параметра, которые не требуют цифр:

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

  2. COND=ONLY Выполнить этот шаг только если предыдущий шаг упал с ошибкой (Abend). Полезно для отправки уведомлений об аварии.

Конструкция IF / THEN / ELSE

В современных версиях z/OS можно использовать более привычную конструкцию IF, которая работает с прямой логикой.

//CHECK IF (STEP1.RC = 0) THEN
//STEP2   EXEC PGM=MYPROG
//      ELSE
//STEP3   EXEC PGM=ERRORHND
//      ENDIF

Это гораздо легче читать, но знание COND обязательно, так как миллионы строк легаси-кода написаны именно с ним.

Переопределение параметров (Region и Time)

В операторе EXEC можно указывать параметры REGION (память) и TIME (время процессора), которые мы изучали в статье про JOB.

Правило простое: EXEC специфичнее JOB.

  • Если REGION указан в JOB, он применяется ко всему заданию.
  • Если REGION указан и в JOB, и в EXEC, то для конкретного шага будет использовано значение из EXEC (в большинстве конфигураций, хотя настройки системы могут менять это поведение).

Пример:

//MYJOB  JOB ... REGION=4M
//STEP1  EXEC PGM=HEAVY,REGION=0M
//STEP2  EXEC PGM=LIGHT
  • STEP1 получит всю доступную память (0M).
  • STEP2 получит 4 мегабайта (наследуется от JOB).

Заключение

Оператор EXEC определяет, что будет делать ваше задание. Мы научились различать программы и процедуры, передавать параметры через PARM и, самое главное, освоили «обратную логику» параметра COND.

Теперь у нас есть «паспорт» (JOB) и «инструкция к действию» (EXEC). Но чего-то не хватает. Программам нужны данные: файлы для чтения, место для записи отчетов. В следующей статье мы разберем третий кит JCL — оператор DD (Data Definition), который связывает логические файлы программы с физическими данными на дисках.

Готовьтесь, будет много информации о датасетах!

Оператор DD: определение данных, работа с файлами и параметрами DISP, SPACE, DCB

Оператор DD: определение данных, работа с файлами и параметрами DISP, SPACE, DCB

Мы продолжаем наш курс по основам JCL. В прошлых статьях мы научились создавать «паспорт» задания с помощью оператора JOB и отдавать команды на запуск программ через EXEC. Но есть одна проблема: программы редко работают в вакууме. Им нужны данные.

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

Сегодня мы разберем третий и последний «кит» JCL — оператор Data Definition. Мы узнаем, как связывать программы с файлами, как создавать новые наборы данных (датасеты) и как управлять их параметрами.

Что такое DD и зачем он нужен?

Оператор DD (Data Definition) связывает логическое имя файла, прописанное внутри программы, с физическим устройством или набором данных (Data Set) на дисках мейнфрейма.

Это разделение — гениальная идея архитекторов z/OS. Программист в коде (например, на COBOL) пишет: «Ччитать данные из файла INPUT». Ему не нужно знать, где именно лежит этот файл, как он называется на диске или сколько места он занимает. Все эти детали указываются в JCL в момент запуска.

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

Анатомия оператора DD

Синтаксис выглядит знакомо:

//DDNAME  DD  ПАРАМЕТРЫ
  • DDNAME (Data Definition Name): Имя, которое начинается с 3-й колонки. Именно это имя ищет программа. Длина — до 8 символов.
  • DD: Операция.
  • Параметры: Описание физического файла.

Рассмотрим ключевые параметры, без которых невозможно представить работу с файлами: DSN, DISP, SPACE и DCB.

1. DSN: Имя набора данных

Параметр DSN (или DSNAME) указывает реальное имя файла в файловой системе z/OS.

//INPUT01  DD  DSN=MY.PROJECT.DATA.FILE

Правила именования файлов в z/OS строгие:

  • Имя состоит из сегментов, разделенных точками.
  • Максимальная длина всего имени — 44 символа.
  • Максимальная длина одного сегмента — 8 символов.
  • Первый символ сегмента должен быть буквой или национальным символом (@, #, $).

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

2. DISP: Жизненный цикл файла

Параметр DISP (Disposition) — один из самых важных. Он говорит системе три вещи:

  1. В каком состоянии файл находится сейчас (до запуска шага)?
  2. Что делать с файлом, если шаг завершится успешно?
  3. Что делать с файлом, если шаг завершится аварийно (Abend)?

Синтаксис:

DISP=(STATUS, NORMAL-TERM, ABNORMAL-TERM)

Первый подпараметр: STATUS (Статус)

Определяет начальное состояние и режим доступа.

Значение Описание
NEW Создать новый файл. Требует параметров SPACE и UNIT.
OLD Файл уже существует. Мы требуем эксклюзивный доступ (никто другой не сможет его читать или писать, пока мы его держим).
SHR (Share) Файл существует. Мы готовы делить его с другими пользователями (обычно для чтения).
MOD Если файл существует — дописать в конец. Если нет — создать новый.

Второй и третий подпараметры: Завершение

Они определяют судьбу файла после работы программы.

Значение Описание
KEEP Оставить файл на диске, но не обновлять каталог (если он не был там).
DELETE Удалить файл физически.
CATLG (Catalog) Сохранить файл и записать информацию о нем в системный каталог. Это самый частый выбор для новых важных файлов.
UNCATLG Удалить запись из каталога, но оставить файл на диске (редко используется).
PASS Передать файл следующему шагу в этом же задании (используется для временных файлов).

Примеры использования DISP

Сценарий 1: Чтение существующего файла

//INPUT  DD DSN=MY.DATA,DISP=SHR

Здесь мы не указываем 2-й и 3-й параметры. По умолчанию SHR подразумевает (SHR,KEEP,KEEP). Мы просто читаем файл.

Сценарий 2: Создание нового файла

//OUTPUT DD DSN=MY.NEW.REPORT,
//          DISP=(NEW,CATLG,DELETE)
  • NEW: Создаем файл.
  • CATLG: Если все прошло хорошо, сохраняем и каталогизируем.
  • DELETE: Если программа упала с ошибкой, удаляем этот «мусор», чтобы не занимать место и не путать нас при перезапуске.

3. SPACE: Выделение места на диске

В отличие от Windows или Linux, в z/OS вы должны заранее сказать системе, сколько места понадобится вашему новому файлу. Если места не хватит, программа упадет с ошибкой x37 (например, B37, D37).

Для этого используется параметр SPACE.

Синтаксис:

SPACE=(ЕДИНИЦА,(ПЕРВИЧНОЕ,ВТОРИЧНОЕ),RLSE)

Единицы измерения

Диски мейнфреймов (DASD) исторически делятся на цилиндры и дорожки.

  • TRK (Tracks): Дорожки. Меньшая единица.
  • CYL (Cylinders): Цилиндры. Большая единица (обычно 1 цилиндр = 15 дорожек).
  • BLK (Block length): Можно указать размер блока в байтах, и система сама посчитает дорожки.

Первичное и вторичное выделение

  • Первичное (Primary): Это место выделяется сразу при создании файла. Даже если вы запишете 1 байт, занято будет всё первичное количество.
  • Вторичное (Secondary): Это «страховка». Если первичное место закончится, система выделит вторичный объем. Если и он закончится — выделит еще раз (до 15 раз, или до 122 экстентов в современных системах).

RLSE (Release)

Очень полезный параметр. Он говорит системе: «После окончания записи освободи всё неиспользованное место». Это помогает экономить дисковое пространство.

Пример:

//OUTPUT DD DSN=MY.LOG,
//          DISP=(NEW,CATLG,DELETE),
//          SPACE=(CYL,(10,5),RLSE)

Читаем: «Выдели 10 цилиндров сразу. Если не хватит, добавляй по 5 цилиндров. В конце верни системе всё, что осталось пустым».

4. DCB: Формат данных

Параметр DCB (Data Control Block) описывает физическую структуру данных внутри файла. Хотя современные системы часто могут определять это автоматически (через SMS — Storage Management Subsystem), знать эти параметры обязан каждый системный программист.

Основные подпараметры:

LRECL (Logical Record Length)

Длина одной записи в байтах. Например, если вы пишете текст шириной 80 символов, то LRECL=80.

RECFM (Record Format)

Формат записи. Определяет, все ли строки имеют одинаковую длину.

  • F (Fixed): Фиксированная длина. Все записи одинаковы.
  • FB (Fixed Blocked): Фиксированная длина, объединенная в блоки для ускорения чтения. Самый популярный формат.
  • V (Variable): Переменная длина. Каждая запись хранит свою длину в начале.
  • VB (Variable Blocked): Переменная длина, блоками.

BLKSIZE (Block Size)

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

Математика эффективности: Чтение с диска — дорогая операция. Если LRECL=80, а BLKSIZE=80, то для чтения 1000 записей диску нужно обратиться 1000 раз. Если LRECL=80, а BLKSIZE=27920 (оптимально для многих дисков), то в одном блоке поместится:

N=2792080=349N = \frac{27920}{80} = 349

Где NN — количество записей в блоке. Теперь для чтения 1000 записей нужно всего 3 обращения к диску. Это в 100 раз быстрее!

Обычно сейчас ставят BLKSIZE=0, и система сама выбирает оптимальный размер.

Пример DCB:

//OUTPUT DD DSN=MY.FILE,
//          DISP=(NEW,CATLG,DELETE),
//          SPACE=(CYL,(1,1)),
//          DCB=(LRECL=80,RECFM=FB,BLKSIZE=0)

Специальные DD имена

Существуют зарезервированные имена и значения, которые вы будете встречать постоянно.

SYSOUT

Используется для вывода логов программы (аналог stdout).

//SYSPRINT DD SYSOUT=*

Звездочка * означает: «Используй тот же класс вывода, что указан в MSGCLASS в операторе JOB».

SYSIN и данные в потоке (Instream Data)

Если вы хотите передать данные программе прямо в тексте JCL, используйте DD *.

//SYSIN    DD *
DATA RECORD 1
DATA RECORD 2
/*

/* в первых двух колонках означает конец данных.

DUMMY

Аналог /dev/null в Linux. Если программа требует файл, но он вам сейчас не нужен, отправьте его в никуда.

//OUTPUT   DD DUMMY

Итоговый пример

Давайте соберем знания из всех статей и напишем шаг копирования файла с помощью утилиты IEBGENER (стандартная утилита копирования).

//COPYJOB  JOB (123),'STUDENT',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBGENER
//* СИСТЕМНЫЕ СООБЩЕНИЯ УТИЛИТЫ
//SYSPRINT DD SYSOUT=*
//* ВХОДНОЙ ФАЙЛ (СУЩЕСТВУЮЩИЙ)
//SYSUT1   DD DSN=OLD.DATA.FILE,DISP=SHR
//* ВЫХОДНОЙ ФАЙЛ (НОВЫЙ)
//SYSUT2   DD DSN=NEW.DATA.FILE,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(5,2),RLSE),
//            DCB=(LRECL=80,RECFM=FB,BLKSIZE=0)
//* УПРАВЛЯЮЩИЕ КОМАНДЫ (ПУСТО, ПРОСТО КОПИРОВАНИЕ)
//SYSIN    DD DUMMY

Заключение

Оператор DD — это мост между абстрактным кодом и реальным железом. Мы разобрали, как именовать файлы (DSN), как управлять их судьбой (DISP), как просить место (SPACE) и как определять формат (DCB).

Теперь вы умеете писать полноценные задания: от заголовка JOB до запуска EXEC и определения данных DD. В следующих статьях мы углубимся в полезные системные утилиты, которые избавят вас от необходимости писать код для рутинных задач копирования и сортировки.

Практика и утилиты: использование процедур, стандартных утилит IBM и отладка ошибок

Практика и утилиты: использование процедур, стандартных утилит IBM и отладка ошибок

Поздравляю! Вы прошли теоретический экватор. Мы уже умеем создавать «паспорт» задания (JOB), запускать программы (EXEC) и управлять данными (DD). Но в реальной жизни писать каждый раз JCL с нуля — это долго, неэффективно и чревато ошибками.

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

Процедуры (PROC): принцип «Напиши один раз, используй везде»

Представьте, что у вас есть сложный шаг задания: запуск программы с десятью входными файлами. Если эту программу нужно запускать в 50 разных заданиях, копировать 20 строк кода в каждое из них — плохая идея. Если имя одного файла изменится, вам придется править 50 файлов.

Для решения этой проблемы в JCL существуют процедуры (Procedures или PROCs). Это именованные блоки JCL-кода, которые можно вызывать одной строкой.

Типы процедур

  1. Каталогизированные процедуры (Cataloged Procedures): Хранятся в специальных системных библиотеках (обычно SYS1.PROCLIB). Они доступны всем пользователям системы.
  2. Внутренние процедуры (Instream Procedures): Описываются прямо внутри вашего задания (в начале JCL) и доступны только этому заданию.

Синтаксис процедуры

Процедура начинается с оператора PROC и заканчивается оператором PEND.

Пример внутренней процедуры:

//MYJOB    JOB (123),'USER',CLASS=A,MSGCLASS=X
//*
//* ОПРЕДЕЛЕНИЕ ПРОЦЕДУРЫ
//MYPROC   PROC
//PRSTEP   EXEC PGM=IEFBR14
//DD1      DD   DSN=MY.DATA,DISP=SHR
//         PEND
//*
//* ВЫЗОВ ПРОЦЕДУРЫ
//STEP1    EXEC MYPROC

Когда система видит EXEC MYPROC, она подставляет вместо этой строки всё содержимое между PROC и PEND.

Символические параметры

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

Определение процедуры с параметром:

//COPYPROC PROC FILE=DEFAULT.FILE
//STEP1    EXEC PGM=IEBGENER
//SYSUT1   DD   DSN=&FILE,DISP=SHR
... остальной код ...
//         PEND

Вызов с заменой параметра:

//RUN1     EXEC COPYPROC,FILE=MY.REAL.DATA

В этом случае &FILE внутри процедуры заменится на MY.REAL.DATA. Если параметр не передать, используется значение по умолчанию (DEFAULT.FILE).

Стандартные утилиты IBM: швейцарский нож мейнфреймщика

Вам не нужно писать программу на COBOL или Assembler, чтобы просто скопировать файл, отсортировать строки или создать пустой датасет. IBM поставляет вместе с z/OS набор мощных утилит.

Рассмотрим «большую тройку», которую должен знать каждый.

1. IEFBR14: Искусство ничего не делать

Название звучит сложно, но это самая простая программа в мире. Она состоит из одной инструкции процессора: «Вернуться обратно» (Branch to Register 14). Она не делает ничего.

Зачем она нужна? Помните оператор DISP? Система создает или удаляет файлы до и после выполнения программы. Запуская IEFBR14, мы заставляем систему обработать карты DD.

  • Создать файл: DISP=(NEW,CATLG)
  • Удалить файл: DISP=(OLD,DELETE)

Пример удаления файла:

//CLEANUP  EXEC PGM=IEFBR14
//DD1      DD   DSN=MY.OLD.FILE,DISP=(MOD,DELETE,DELETE),
//              UNIT=SYSDA,SPACE=(TRK,0)

2. IEBGENER: Копирование данных

Утилита для копирования последовательных наборов данных (Sequential Datasets). Она берет данные из SYSUT1 и перекладывает их в SYSUT2.

Пример копирования файла:

//COPY     EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD DSN=INPUT.FILE,DISP=SHR
//SYSUT2   DD DSN=OUTPUT.FILE,DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,5),RLSE),DCB=*.SYSUT1
//SYSIN    DD DUMMY
  • SYSUT1: Откуда берем (Input).
  • SYSUT2: Куда кладем (Output).
  • SYSPRINT: Куда писать отчет о работе.
  • SYSIN: Управляющие команды (DUMMY, если просто копируем всё подряд).

3. DFSORT (или SORT): Сортировка и фильтрация

Мощнейшая утилита для сортировки, слияния и фильтрации данных. Она управляется через SYSIN.

Пример: Отсортировать файл по фамилии (начинается с 1-й колонки, длина 20 символов).

//MYSORT   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=UNSORTED.DATA,DISP=SHR
//SORTOUT  DD DSN=SORTED.DATA,DISP=(NEW,CATLG,DELETE),...
//SYSIN    DD *
  SORT FIELDS=(1,20,CH,A)
/*

Команда SORT FIELDS=(1,20,CH,A) означает:

  • 1: Начать с 1-го байта.
  • 20: Длина поля 20 байт.
  • CH: Тип данных — символы (Character).
  • A: По возрастанию (Ascending).

Отладка: Что делать, когда всё сломалось?

В мире мейнфреймов ошибки делятся на два больших лагеря:

  1. JCL Errors: Синтаксические ошибки. Задание даже не начало выполняться. Вы забыли пробел, написали DISS вместо DISP или указали несуществующий файл с DISP=OLD.
  2. ABEND (Abnormal End): Задание запустилось, но упало в процессе. Это сопровождается кодом ошибки.

Топ-5 кодов ABEND, которые нужно знать в лицо

Коды ошибок в z/OS выглядят как Sxxx (System code) или Uxxxx (User code). Вот самые частые системные коды:

S013 (Open Error)

Проблема: Ошибка при открытии файла. Причина: Часто это несоответствие параметров DCB. Например, программа ожидает файл с длиной записи 80 байт (LRECL=80), а вы подсунули ей файл с длиной 100.

S806 (Not Found)

Проблема: Система не может найти программу, указанную в PGM=.... Причина: Опечатка в имени программы или вы забыли указать библиотеку (STEPLIB), где эта программа лежит.

Sx37 (B37, D37, E37) — Space Abends

Это классика. Ошибка нехватки места на диске.

  • B37: Закончилось место при записи (не хватило первичного и вторичного пространства).
  • D37: Закончилось место при первичном выделении (редко, но бывает).
  • E37: Закончилось место в директории PDS (Partitioned Data Set).

Решение: Увеличьте значения в параметре SPACE или добавьте RLSE.

S322 (Time Out)

Проблема: Задание работало дольше, чем разрешено. Причина: Либо программа попала в бесконечный цикл, либо вы задали слишком жесткий лимит в параметре TIME.

S222 (Cancelled)

Проблема: Задание было принудительно отменено. Причина: Оператор или вы сами (через команду cancel) остановили выполнение задания.

Алгоритм поиска ошибки

  1. Откройте JESMSGLG (системный лог).
  2. Идите в самый низ и ищите сообщение, начинающееся с IEF450I (информация о завершении).
  3. Найдите код ABEND (например, S806).
  4. Если это JCL Error, ищите сообщение с текстом ERROR или стрелочкой, указывающей на конкретную строку кода.

Заключение курса

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

Что делать дальше? Лучший способ выучить JCL — это практика. Попробуйте написать задание, которое:

  1. Создает файл с помощью IEFBR14.
  2. Копирует в него данные с помощью IEBGENER.
  3. Сортирует результат с помощью DFSORT.

Удачи в освоении z/OS!