I don't know of a way except internal reader as well - except you run a TSO-callable program like DFSORT. In this case, you could avoid the internal reader, which might help you if you run job streams thru scheduling tools (see sample below). We use this logic often to concatenate an unknown/variable number of //SORTIN -
DSNs within one job.
hth
//SETLVL SET FNAME=QC41223
//*
//STEP0000 EXEC PGM=IEFBR14 *This step is for test only - delete datasets allocated later on
//MODELDSN DD DSN=T750K.MODEL.DATA,DISP=(MOD,DELETE,DELETE),
// UNIT=SYSDA,SPACE=(CYL,(1),RLSE),LRECL=80,RECFM=FB,BLKSIZE=0
//RERUNDSN DD DSN=T750K.TEST.QC41223.CSV.DATA,DISP=(MOD,DELETE,DELETE),
// UNIT=SYSDA,SPACE=(CYL,(1),RLSE),LRECL=80,RECFM=FB,BLKSIZE=0
//*
//S1 EXEC PGM=SORT,PARM='JP1"&FNAME..CSV"'
//SYSOUT DD SYSOUT=*
//SORTIN DD *
ERC.CSV
//SORTOUT DD DSN=&&NEWDSN1,DISP=(,PASS),
// UNIT=VIO,SPACE=(1,(1),RLSE),LRECL=80,RECFM=FB
//SYSIN DD *
INREC FINDREP=(IN=C'ERC.CSV',OUT=JP1)
OPTION COPY
OUTREC BUILD=(3:C'DA(''T750K.TEST.',JP1,C'.DATA'') +',
80:X)
/*
//STEP0010 EXEC PGM=IEFBR14
//MODELDSN DD DSN=T750K.MODEL.DATA,DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,SPACE=(CYL,(1),RLSE),LRECL=80,RECFM=FB,BLKSIZE=0
//*
//STEP0300 EXEC PGM=IKJEFT01,DYNAMNBR=25
//SYSTSPRT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SORTIN DD *
Dummy Record
//SYSIN DD *
OPTION COPY
//SYSTSIN DD *
ALLOC FI(SORTOUT) +
// DD DISP=SHR,DSN=&&NEWDSN1
// DD *
NEW LIKE('T750K.MODEL.DATA')
CALL 'SYS1.SICELINK(ICEMAN)'
/*