SYMNAMES: Nombres simbólicos en un Sort

En este artículo vamos a introducir el uso de nombres simbólicos (SYMNAMES) en un SORT para hacer referencia a campos y constantes. Se trata de una facilidad que hace más legibles  los Sort y facilita su mantenimiento.

Por ejemplo, supongamos que tenemos un fichero con la siguiente estructura:

Campo Pos. Inicio Longitud Formato COBOL
COD_FACTURA 1 8 X(8)
CENTRO 9 4 X(4)
FECHA 13 10 X(10)
COD_CLIENTE 23 8 X(8)
IMP_FACTURA 31 8 9(6)V9(2)

Queremos obtener las facturas del centro ‘2040’ (Madrid),  que se hayan generado durante el periodo de rebajas (del 07/01/2013 al 10/02/2013)  y cuyo importe sea igual o superior a 500.

El SORT  para conseguirlo sería el siguiente:

//SELECCI  EXEC PGM=SORT,COND=(0,NE)
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=A144759.SYMNAMES.SMSSTD,DISP=SHR
//SORTOUT  DD DSN=A144759.SYMNAMES.SMSSTD.SORT,
//         DISP=(NEW,CATLG,DELETE),LRECL=38,RECFM=FB,
//         DSORG=PS
//SYSIN    DD *
SORT FIELDS=COPY
INCLUDE COND=(9,4,CH,EQ,C'2040',AND,
13,10,CH,GE,C'2013-01-07',AND,
13,10,CH,LE,C'2013-02-10',AND,
31,8,ZD,GT,+50000)

Si cambia la estructura del fichero, tendremos que recalcular las posiciones de inicio de los campos afectados y sustituirlas en todas las condiciones en que aparezcan (en este Sort y en cualquier otro que utilice el mismo fichero).

Lo mismo ocurrirá si cambia alguna de las constantes; por ejemplo, si queremos cambiar la fecha de fin del periodo de rebajas, tendremos que sustituirla en todas las condiciones de todos los Sort en que aparezca.

Estos inconvenientes podemos evitarlos mediante el uso de SYMNAMES. Veámoslo mediante una nueva versión del SORT anterior en la que hemos sustituido los nombres de campos y las constantes por nombres simbólicos.

//SELECCI  EXEC PGM=SORT,COND=(0,NE)
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=A144759.SYMNAMES.SMSSTD,DISP=SHR
//SORTOUT  DD DSN=A144759.SYMNAMES.SMSSTD.SORT,
//         DISP=(NEW,CATLG,DELETE),LRECL=38,RECFM=FB,
//         DSORG=PS
//SYMNAMES DD *
*CAMPOS
COD_FACTURA,1,8,CH
CENTRO,*,4,CH
FECHA,*,10,CH
COD_CLIENTE,*,8,CH
IMPORTE,*,8,ZD
*CONSTANTES
MADRID,'2040'
INI_REBA,'2013-01-07'
FIN_REBA,'2013-02-10'
TOPE_FACT,50000
//SYSIN    DD *
SORT FIELDS=COPY
INCLUDE COND=(CENTRO,EQ,MADRID,AND,
FECHA,GE,INI_REBA,AND,
FECHA,LE,FIN_REBA,AND,
IMPORTE,GT,TOPE_FACT)

En este ejemplo hemos incluido los nombres simbólicos directamente en la ficha SYMNAMES para mayor claridad, pero lo lógico es incluirlos en un fichero. De esa forma podremos reutilizarlos en cualquier otro Sort que utilice el fichero referenciado.  Además será posible hacer modificaciones en la estructura del fichero sin tener que cambiar los Sort.

Vemos que es posible incluir la posición de inicio y la longitud del campo, o bien indicar que comienza en la posición siguiente al campo anterior mediante un asterisco.



Uso de variables en un JCL

Para definir una variable en un JCL y asignarle valor se utiliza el comando SET:

// SET PROGRAMA=CARGACLI
// SET FICHENT=FF.CLIENTES.ENT
// SET FICHSAL=FF.CLIENTES.SAL
// SET NUEVO='DISP=(NEW,CATLG,DELETE)'

Las variables así definidas se pueden usar en cualquiera de los siguientes pasos del JCL añadiendo un ‘&’ a su nombre:

//CLIENTES  EXEC PGM=&PROGRAMA
//SYSOUT    DD SYSOUT=*
//ENTRADA   DD DSN=&FICHENT,DISP=SHR
//SALIDA    DD DSN=&FICHSAL,&NUEVO,
//                 SPACE=(CYL,(5,1),RLSE),
//                 DCB=(LRECL=350,RECFM=FB)

Es importante no confundir las variables de JCL con las variables y constantes simbólicas de los planificadores (Control-M, TWS-OPC, etc.). Estas últimas van precedidas por ‘%%’ y se definen dentro de un comentario.  Por ejemplo, así se define una variable de Control-M:

//* %%SET %%FECHASTA  =  2013-04-03

Esta variable podríamos utilizarla en el INCLUDE de un Sort:
INCLUDE COND=(1,10,LE,C'%%FECHASTA')

Pero a diferencia de las variables de JCL, estas variables solo serán sustituidas por su valor si lanzamos el job a través de Control-M. Si lo lanzamos mediante un SUBMIT se producirá un JCL ERROR.

Comprobar si un fichero está vacío con ICETOOL

Al diseñar un JCL, muchas veces interesa que ciertos pasos no se ejecuten si un determinado fichero está vacío, algo que podemos comprobar fácilmente mediante el operador COUNT de ICETOOL:

//-----------------------------
//PASO1 EXEC PGM=ICETOOL
//IN DD DSN=FICHERO,DISP=SHR
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//TOOLIN DD DATA
COUNT FROM(IN) EMPTY
/*
//-----------------------------
//INICOND IF PASO1.RC=0  THEN
//PASO2   EXEC PGM=NOVACIO
//FINCOND ENDIF

PASO1 devolverá código de retorno 12 si el fichero está vacío, lo que nos permite controlar mediante IF o COND la ejecución del resto de pasos. En este ejemplo, PASO2 se ejecutaría sólo si el fichero no está vacío.

12 es el código de retorno por defecto, pero es posible cambiarlo a 8 o 4:

COUNT FROM(IN) EMPTY RC8
COUNT FROM(IN) EMPTY RC4

También se puede invertir la condición para que el código de retorno distinto de 0 se devuelva si el fichero NO está vacío:

COUNT FROM(IN) NOTEMPTY RC4

Devolvería código de retorno 4 si el fichero tiene datos y 0 si está vacío.

Condicionar pasos de un JCL: parámetro COND


En este artículo vamos a examinar la forma “clásica” de condicionar la ejecución de un paso de JCL: el parámetro COND.

Al incluir COND en un paso, podemos comprobar el código de retorno de uno o varios pasos anteriores mediante expresiones relacionales. Si alguna de las expresiones es verdadera, entonces el paso NO se ejecuta y el JCL continúa por el siguiente.

Si un paso termina anormalmente debido a un abend, lo habitual es que el sistema cancele la ejecución de los siguientes pasos.  Podemos cambiar este comportamiento mediante el parámetro COND=EVEN, que indica que el paso se debe ejecutar aunque se haya producido un abend.  COND=ONLY provoca que el paso se ejecute sólo en caso de abend.

El parámetro COND también se puede usar a nivel de job. En este caso la condición se evalúa para cada paso del JCL. En caso de que se cumpla para algun paso, terminará la ejecución del job.

Se utiliza COND a nivel de paso en vez de a nivel de job en los siguientes casos:

  • Si las comprobaciones de los códigos de retorno son diferentes para cada paso
  • Cuando queremos comprobar el código de retorno de un paso concreto.
  • Si queremos incluir una condición de abend con ONLY o EVEN.
  • Cuando queremos que se salte un único paso.

Veámoslo con ejemplos:

1)  A nivel de job

//P1 JOB ,’PRUEBA’,COND=((10,GT),(20,LT))

Antes de la ejecución de cada paso se comprobará si se cumple la condición:

10 > CR OR 20 < CR

Siendo CR el retorno del paso precedente. Si la condición es verdadera, terminará la ejecución del job.

2) A nivel de paso con condición general

//PASO2 EXEC PGM=PROGR2,COND=(1,LT)

Saltar PASO2  si se cumple 1 < CR para todos los pasos anteriores. La ejecución continuará por el siguiente paso.

3) A nivel de paso con condición de paso

//PASO3  EXEC PGM=PROGR3,COND=(4,EQ,PASO1)

Saltar PASO3 si se cumple CR = 4 para PASO 1. La ejecución continuará por el siguiente paso.

4) con COND=EVEN

//PASO4 EXEC PGM=PROGR4,COND=(EVEN)

PASO4 se ejecutará  tanto si los pasos anteriores han terminado con éxito como si se ha producido algún abend.

5) Con COND=ONLY

//PASOER EXEC PGM=PROGERR,COND=(ONLY)

PASOER se ejecutará  sólo si se ha producido un abend en alguno de los pasos anteriores.

6)EVEN u ONLY combinados con otra condición

//PASO6 EXEC PROC=PROGR6,COND =((20,LT),EVEN)

Saltar PASO6  si 20 < CR para cualquiera de los pasos anteriores. Si no es así,  se ejecutará siempre (incluso en caso de abend).

//PASO7 EXEC PROC=PROGR7,COND =((10,LE),ONLY)

PASO7 se ejecutará sólo en caso de abend, siempre que además no se cumpla 10 <= RC para todos los pasos anteriores.



Condicionar pasos de un JCL: estructura IF/THEN/ELSE/ENDIF

Para condicionar la ejecución de un paso de un JCL al resultado de los pasos anteriores tenemos dos opciones:

  • La estructura IF/THEN/ELSE/ENDIF
  • El parámetro COND

En este artículo vamos a analizar la primera de ellas, que es la más sencilla y la recomendada por IBM en su manual oficial. Su sintaxis es la siguiente:

//IF (expresión relacional) THEN
//Paso/s a ejecutar si la expresión es verdadera

//ELSE  
//Paso/s a ejecutar si la expresión es falsa
// ENDIF

Podemos ver que consta de

  • Una expresión relacional en la que podemos usar los operadores &(AND), | (OR) y ¬ (NOT), >, <, >=, <= y =.
  • Una serie de pasos a ejecutar si la expresión se evalúa a true (verdadero)
  • Una serie de pasos a ejecutar si la expresión se evalúa a false (falso)

La forma de codificar la expresión condicional determina cómo se evalúa la expresión condicional:

  • Evaluación a nivel de job:si no incluimos un nombre de paso, la expresión se evaluará para a todos los pasos anteriores del job.
  • Evaluación a nivel de paso:si incluimos un nombre de paso, la expresión se evaluará sólo para el paso indicado.

En caso de que algún paso del job termine anormalmente, lo habitual es que el sistema no ejecute los restantes pasos y finalice la ejecución del job. Sin embargo, la estructura IF/THEN/ELSE/ENDIF nos permite ejecutar un paso incluso cuando alguno de los pasos anteriores haya abendado.

El control de abends se puede hacer de dos formas:

  • Comprobando si se ha producido un abend (cualquiera): IF (ABEND)
  • Comprobando si se ha producido un código de abend concreto: Por ejemplo, IF (ABENDCC=S0C4) para detectar un S0C4.

Vamos a ver varios ejemplos de utilización de la estructura:

1)     Ejecutar PASO2 sólo si el código de retorno de PASO1 es 5. Si no, saltar PASO2 y ejecutar directamente PASO3.
//PASO1    EXEC PGM=PROGRAMA1
//*-----------------------------------------
//  IF (PASO1.RC=05) THEN
//*-----------------------------------------
//PASO2    EXEC PGM=PROGRAMA2
//*-----------------------------------------
//  ELSE
//*-----------------------------------------
//PASO3   EXEC PGM=PROGRAMA3
//*-----------------------------------------
//  ENDIF

2)     Ejecutar el paso CONTERR sólo si el código de retorno de alguno de los pasos anteriores es mayor  que 4. En caso contrario, continuar directamente al paso SIGUIENTE.
//*-----------------------------------------
//  IF (RC > 4) THEN
//*-----------------------------------------
//CONTERR   EXEC PGM=ERRORES
//*-----------------------------------------
//  ELSE
//*-----------------------------------------
//SIGUIENTE  EXEC …
//*-----------------------------------------
//  ENDIF

3)     Ejecutar el paso RETESP sólo si el retorno del paso 1 es mayor que 20 o el del paso 2 es igual a 10. Si no, continuar directamente al paso SIGUIENTE.
//*-------------------------------------------
//  IF (PASO1.RC > 20 | PAS02.RC = 10) THEN
//*-------------------------------------------
//RETESP  EXEC PGM=ERRORES
//*-------------------------------------------
//  ENDIF
//*-------------------------------------------
//SIGUIENTE  EXEC …

4)     Ejecutar PASOABE si se ha producido un abend en alguno de los pasos anteriores.
//*-------------------------------------------
//  IF (ABEND)
//*-------------------------------------------
//PASOABE EXEC PGM=RUTERR
//*-------------------------------------------
//  ENDIF

5)     Ejecutar PASOABE si se ha producido un abend S0C4 en PASO3.
//*-------------------------------------------
//  IF (PASO3.ABENDCC=S0C4)
//*-------------------------------------------
//PASOABE EXEC PGM=RUTERR
//*-------------------------------------------
//  ENDIF



Cruce de ficheros con Joinkeys avanzado

En un artículo anterior vimos un ejemplo de Joinkeys en su forma más básica. A continuación vamos a introducir el resto de posibilidades que ofrece esta aplicación que se ejecuta dentro de la utilidad sort del mainframe (tanto DFSORT como su clon Syncsort)

JNF1CNTL Y JNF2CNTL: DD para incluir sentencias de control (INCLUDE, OMIT, INREC y SUM) para los ficheros 1 y 2 respectivamente.

SORTED: Indica que el fichero de entrada se encuentra ordenado y no es necesario el proceso de ordenación.

JOIN: Permite especificar el tipo de cruce. Si no la incluimos, se hará el cruce básico que vimos en el ejemplo anterior, que genera un fichero con los registros que cruzan. Las otras opciones son:

  • UNPAIRED,F1,ONLY

Solo los registros del fichero 1 que no cruzan.

  • UNPAIRED,F2,ONLY

Solo los registros del fichero 2 que no cruzan.

  • UNPAIRED,F1

Registros que cruzan más registros del fichero 1 que no cruzan.

  • UNPAIRED,F2

Registros que cruzan más registros del fichero 2 que no cruzan.

  • UNPAIRED,F1,F2 o UNPAIRED

Registros que cruzan más registros que no cruzan de ambos ficheros de entrada.

  • UNPAIRED,F1,F2 ONLY o UNPAIRED ONLY

Solo los registros que no cruzan de ambos ficheros de entrada.

Para ver un caso de utilización de estas nuevas opciones, vamos a incluirlas en el ejemplo del artículo anterior sobre enfrentamiento. Recordamos que se trataba de dos ficheros de entrada de 10 posiciones con la clave en las 3 primeras. El primero de ellos contenía nombres de personas y el segundo teléfonos. Cuando las claves de los ficheros 1 y 2 coincidían queríamos escribir en salida un registro con la clave, el nombre y el teléfono.

Ahora sabemos que los ficheros de entrada están ordenados, por lo que usamos la opción SORTED, que evita la ordenación inicial. En salida queremos obtener no solo los registros que cruzan sino también los no emparejados de ambos ficheros (por ejemplo, para llevar un control de errores). Para ello empleamos la opción UNPAIRED,F1,F2. Además queremos aplicar un filtro sobre el segundo fichero de entrada para que solo entren en el cruce los teléfonos que empiecen por 9. Lo indicaremos mediante la DD JNF2CNTL.

Para poder identificar la procedencia de los registros de salida, hay que añadirle el carácter de control ‘?’ al fichero de cruce. El proceso de cruce informará el caracter de control con ‘1’, ‘2’ o ‘B’ dependiendo de si la clave se encuentra solo en el fichero 1, solo en el 2 o en ambos.

Una vez realizado el cruce, el fichero generado pasa por nuevo sort en el que podemos reordenar el fichero de salida, formatearlo, etc. En el ejemplo no lo ordenamos (SORT FIELDS=COPY) , pero añadimos tres sentencias OUTFIL para generar tres ficheros diferentes en función de la procedencia (cruzan, solo en fichero 1 y solo en fichero 2)

//CRUCE  EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTJNF1 DD DSN=ENTRADA1,DISP=SHR
//SORTJNF2 DD DSN=ENTRADA2,DISP=SHR
//F1SOLO   DD DSN=SALIDA1,
//            DISP=(,CATLG,DELETE),SPACE=(TRK,(1,1),RLSE),
//            DCB=(RECFM=FB,LRECL=10,BLKSIZE=0)
//F2SOLO   DD DSN=SALIDA2,
//            DISP=(,CATLG,DELETE),SPACE=(TRK,(1,1),RLSE),
//            DCB=(RECFM=FB,LRECL=10,BLKSIZE=0)
//AMBOS    DD DSN=SALIDA3,
//            DISP=(,CATLG,DELETE),SPACE=(TRK,(1,1),RLSE),
//            DCB=(RECFM=FB,LRECL=17,BLKSIZE=0)
//JNF2CNTL  DD *
  INCLUDE COND=(4,1,CH,EQ,C'9')
//SYSIN DD *
  JOINKEYS FILE=F1,FIELDS=(1,3,A),SORTED
  JOINKEYS FILE=F2,FIELDS=(1,3,A),SORTED
  JOIN UNPAIRED,F1,F2
  REFORMAT FIELDS=(F1:1,10,F2:1,10,?)
* ‘?’: carácter de control para distinguir los registros de salida
*    ‘1’: solo en fichero 1 
*    ‘2’: solo en fichero 2
*    ‘B’: en ambos 
  SORT FIELDS=COPY
  OUTFIL FNAMES=F1SOLO,INCLUDE=(21,1,CH,EQ,C'1'),
  BUILD=(1,10)
  OUTFIL FNAMES=F2SOLO,INCLUDE=(21,1,CH,EQ,C'2'),
  BUILD=(11,10)
  OUTFIL FNAMES=AMBOS,INCLUDE=(21,1,CH,EQ,C'B'),
  BUILD=(1,10,14,7)

<

Clases de datos en COBOL

Cobol nos ofrece una serie de clases de datos predefinidas que podemos utilizar en expresiones lógicas llamadas condiciones de clase. La utilidad de estas condiciones es comprobar si todos los caracteres EBCDIC de una variable alfanumérica pertenecen a una determinada clase. Las clases predefinidas son: NUMERIC, ALPHABETIC, ALPHABETIC-LOWER  (minúsculas) y ALPHABETIC-UPPER (mayúsculas).

IF VARIABLE-A IS ALPHABETIC-LOWER THEN ...

La condición de clase anterior será verdadera si todos los caracteres de VARIABLE-A son minúsculas.

También es posible crear nuevas clases de datos, que se definen en el párrafo SPECIAL-NAMES. Por ejemplo:

SPECIAL-NAMES.

CLASS LETRA-VALIDA IS 'a' THRU 'f'. (letras 'a','b','c','d','e' y 'f')
CLASS MAYOR-MENOR IS '<' '>'.
CLASS PARENTESIS IS '(' ')'.

Cruce de ficheros con Joinkeys básico

El cruce de ficheros en un JCL se realiza mediante la utilidad Joinkeys, que se ejecuta a través de DFSORT. Está disponible en z/OS desde 2009. Hasta entonces solo era posible enfrentar ficheros mediante el operador Splice de Icetool.

Veamos un ejemplo muy sencillo de Joinkeys para cruzar dos ficheros y generar un fichero de salida con los registros que cruzan.

Tenemos dos ficheros de entrada de 10 posiciones . El primero contiene nombres de personas y el segundo los teléfonos de las mismas. Están relacionados por un código de persona que se encuentra en las tres primeras posiciones de ambos ficheros.

Queremos generar un fichero de salida que contenga un registro de salida por persona con la clave, el nombre y el teléfono.

No es necesario que los ficheros de entrada estén ordenados, ya que la utilidad los ordena automáticamente por las posiciones de cruce.

El paso de ejecución de Sort completo sería el siguiente:

//CRUCE EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTJNF1 DD DSN=FIC.ENTRADA1,DISP=SHR
//SORTJNF2 DD DSN=FIC.ENTRADA2,DISP=SHR
//SORTOUT DD DSN=FIC.SALIDA,DISP=(,CATLG,DELETE)
//SYSIN DD *
JOINKEYS FILE=F1,FIELDS=(1,3,A)
JOINKEYS FILE=F2,FIELDS=(1,3,A)
REFORMAT FIELDS=(F1:1,10,F2:4,7)
SORT FIELDS=COPY

Las fichas SORTJNF1 y SORTJNF2 contienen los ficheros de entrada, mientras que en SORTOUT se define el fichero de salida, como en cualquier Sort.

SYSIN contiene los parametros del proceso, que podemos dividir en tres bloques:

  1. JOINKEYS FILE: posiciones por las que se ordenará cada fichero de entrada antes del cruce, que serán también las de enfrentamiento. También se indica el criterio de ordenación (A: ascendente; D: descendente) .
  2. REFORMAT FIELDS: formato del fichero resultado del cruce, que puede contener posiciones de cualquiera de los dos ficheros. En el ejemplo, las 10 primeras del fichero 1 y 7 posiciones a partir de la cuarta del fichero 2.
  3. SORT final: el fichero resultado del cruce pasa por un Sort que nos permite cualquier tipo de formateo. En el ejemplo no es necesario, por lo que simplemente copiamos el fichero con un SORT FIELDS=COPY.

En el siguiente artículo se explican más opciones de esta utilidad, que no se limita a lo mostrado en este ejemplo. Podemos obtener en salida tanto los registros que cruzan como los que no, filtrar los ficheros de entrada antes del cruce, etc.



Cruce de ficheros con ICETOOL (Splice)

Si queremos montar un JCL que cruce ficheros mediante SORT, sin utilizar un programa adicional, tenemos varios opciones. Una de ellas es emplear el operador SPLICE del ICETOOL, disponible desde la actualización de DFSORT de 2003.

Veamos un ejemplo extraído del manual Smart DFSORT Tricks de IBM.

Tenemos dos ficheros con dos listas de nombres:

Fichero1
Vicky
Frank
Carrie
Holly
David

Fichero2
Karen
Holly
Carrie
Vicky
Mary

Y queremos crear ficheros de salida que contengan:

– Los nombres que aparecen en Fichero1 y Fichero2

– Los nombres que aparecen solo en Fichero1

– Los nombres que aparecen solo en Fichero2

Podemos hacerlo mediante el SPLICE, utilizando el truco de añadir el literal ‘11’ a los registros del primer fichero y el literal ‘22’ a los del segundo:

//S1 EXECPGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//IN1 DD DSN=… Fichero1
//IN2 DD DSN=… Fichero2
//T1  DD DSN=&&T1,UNIT=SYSDA,SPACE=(TRK,(5,5)),DISP=(MOD,PASS)
//OUT12 DD SYSOUT=*
//OUT1 DD SYSOUT=*
//OUT2 DD SYSOUT=*
//TOOLIN DD *
//* Copiamos Fichero1 a un fichero temporal
COPY FROM(IN1) TO(T1) USING(CTL1)
//* Copiamos Fichero2 a un fichero temporal
COPY FROM(IN2) TO(T1) USING(CTL2)
SPLICE FROM(T1) TO(OUT12) ON(1,10,CH) WITH(13,1) –
USING(CTL3) KEEPNODUPS
//CTL1CNTL DD *
//* Marcar los registros de Fichero1 con ‘11’
INREC OVERLAY=(12:C’11’)
//CTL2CNTL DD *
//* Marcar los registros de Fichero2 con ‘22’
INREC OVERLAY=(12:C’22’)
//CTL3CNTL DD *
//* Registros que están en los dos ficheros
OUTFIL FNAMES=OUT12,INCLUDE=(12,2,CH,EQ,C’12’),BUILD=(1,10)
//* Registros que solo estan en Fichero1
OUTFIL FNAMES=OUT1,INCLUDE=(12,2,CH,EQ,C’11’),BUILD=(1,10)
//* Registros que solo estan en Fichero2
OUTFIL FNAMES=OUT2,INCLUDE=(12,2,CH,EQ,C’22’),BUILD=(1,10)

El operador SPLICE ordena los registros del fichero temporal por los campos del ON. Al generar la salida, si hay duplicados se coloca en primer lugar el registro del primer fichero (registro base) y a continuación el que corresponde al segundo fichero (registro duplicado), que es igual al registro base salvo en las posiciones indicadas en el WITH, que se toman del segundo fichero.

Contenido de los ficheros de salida:

OUT12
Carrie
Holly
Vicky

OUT1
David
Frank

OUT2
Karen
Mary

¿El COBOL para z/OS puede manejar XML?

La respuesta es sí.

Parece que la idea que se tiene de COBOL como un viejo dinosaurio que lleva decadas sin actualizarse no es del todo exacta.

En las últimas versiones del COBOL para z/OS, IBM ha introducido instrucciones específicas para generar el equivalente XML de una estructura de datos COBOL y a la inversa.

Estas instrucciones son:

XML GENERATE

Genera el equivalente XML de una estructura COBOL.

XML PARSE

Procesa un documento XML generando un evento diferente para cada componente del documento. Estos eventos pueden ser tratados por un procedimiento (párrafo) COBOL de usuario.

Más información en la documentación oficial de IBM:

http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/igy3lr50/6.2.41?DT=20090821081020

http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/igy3lr50/6.2.42?DT=20090821081020