On the mainframe, datasets are described by a record format (RECFM) and logical record length (LRECL). The two most common formats for DFSORT work are FB (Fixed Block) and VB (Variable Block). FB means every record has the same length; VB means each record can have a different length, with a 4-byte Record Descriptor Word (RDW) at the start of each record. Choosing the right format affects storage, performance, and compatibility with downstream programs. This page compares VB and FB, explains how DFSORT handles each, when to use which, and how to convert between them.
FB stands for Fixed Block. "Fixed" means every logical record has the same length; "Block" means multiple records are grouped into a single physical block on disk. So with RECFM=FB and LRECL=80, every record is exactly 80 bytes, and the system packs as many 80-byte records as fit into each block (e.g. BLKSIZE=27920 gives 349 records per block). There is no length prefix per record—the length is defined once in the DCB (LRECL).
VB stands for Variable Block. "Variable" means each record can have a different length; "Block" again means multiple records per physical block. Each record starts with a 4-byte RDW that contains the total length of that record (including the RDW). So the layout is: RDW (4 bytes) + data (variable bytes). The system uses the RDW to know how many bytes to read for that record. LRECL for VB is the maximum record length (including the 4-byte RDW), so the maximum data length is LRECL minus 4.
RECFM can be a combination of characters. The main ones for DFSORT are:
| RECFM | Meaning |
|---|---|
| F | Fixed length, unblocked (one record per block). Rarely used. |
| FB | Fixed length, blocked. Multiple records per block. Most common for fixed-length files. |
| FBS | Fixed Block Standard: fixed length, blocked, with optional span (large records). |
| V | Variable length, unblocked (one record per block). |
| VB | Variable length, blocked. Multiple records per block. Standard for variable-length files. |
| VBS | Variable Block Standard: variable length, blocked, with span support for very long records. |
For typical DFSORT jobs, you use RECFM=FB when all records have the same length and RECFM=VB when record lengths vary. The "B" (blocked) form is preferred over F or V alone because blocking reduces I/O by reading and writing multiple records per physical block.
DFSORT reads SORTIN and writes SORTOUT according to the dataset's RECFM and LRECL (from the DD statement or catalog). For fixed-length (FB) input, it reads exactly LRECL bytes per record. There is no RDW; position 1 in control statements is the first byte of the record. For variable-length (VB) input, it reads the 4-byte RDW, uses it to get the record length, then reads the data. Positions in SORT FIELDS, INREC, OUTREC, INCLUDE, and OMIT refer to the data portion only—position 1 is the first byte after the RDW. DFSORT does not strip the RDW on input or add it on output; it keeps the VB format intact when reading and writing VB.
When you allocate SORTOUT, you must set RECFM and LRECL to match what you are writing. If you use OUTREC to build a 60-byte fixed record, allocate SORTOUT with RECFM=FB and LRECL=60. If you write variable-length records (e.g. VB in, VB out with no length change), use RECFM=VB and an LRECL at least as large as the longest record.
LRECL is the length in bytes of every record. Example: LRECL=80 means every record is exactly 80 bytes. There is no per-record length field; the DCB defines the length once. When you change the output record length with OUTREC (e.g. build a 60-byte record from an 80-byte input), you must set SORTOUT LRECL=60 to match.
LRECL is the maximum record length, and that maximum includes the 4-byte RDW. So LRECL=256 means the longest record can be 256 bytes total: 4 bytes for the RDW plus up to 252 bytes of data. Shorter records are allowed; each record's actual length is stored in its RDW. When allocating a VB dataset for DFSORT output, set LRECL to the maximum record length you will write (including the RDW).
| Aspect | FB | VB |
|---|---|---|
| Record length | Same for every record (LRECL). | Varies per record; max = LRECL (incl. 4-byte RDW). |
| Per-record overhead | None. | 4-byte RDW per record. |
| Position 1 in DFSORT | First byte of the record. | First byte of data (after RDW). |
| Space for short records | Padded to LRECL; can waste space. | Only as long as needed; saves space. |
| Typical use | Card image, fixed layout, reports, legacy I/O. | Variable text, logs, variable-length output. |
You can produce fixed-length output from variable-length input by building a fixed-length record in OUTREC (or INREC) and allocating SORTOUT with RECFM=FB and the correct LRECL. For example, take the first 80 data bytes and pad with spaces if the input record is shorter; then SORTOUT with RECFM=FB, LRECL=80. Conversely, you can produce variable-length output from fixed-length input by building records of varying length and writing to a RECFM=VB dataset (with appropriate LRECL for the maximum). DFSORT's OUTFIL also supports VTOF (variable to fixed) and FTOV (fixed to variable) options for conversion when using multiple output files.
Example: copy variable-length input to a fixed 80-byte output (pad with spaces). You might use INREC or OUTREC to take bytes 1–80 of the data (or 1–L where L is the data length) and pad to 80, then allocate SORTOUT with RECFM=FB, LRECL=80. The exact build syntax depends on how you want to handle records shorter than 80 bytes (e.g. pad right with spaces).
123OPTION COPY OUTREC FIELDS=(1,80) * or use BUILD with padding for short records * SORTOUT: RECFM=FB, LRECL=80
For VB input, positions in OUTREC refer to the data portion; 1,80 takes the first 80 data bytes. If the record has fewer than 80 data bytes, you may need BUILD with conditional logic or a product-specific way to pad. For simple "first 80 bytes, pad if shorter", refer to your DFSORT documentation for the exact BUILD or IFTHEN syntax.
In your JCL, SORTIN and SORTOUT need the correct DCB. For fixed-length:
123//SORTIN DD DSN=MY.FB.FILE,DISP=SHR //SORTOUT DD DSN=MY.FB.OUT,DISP=(NEW,CATLG), // SPACE=(CYL,(10,5)),DCB=(RECFM=FB,LRECL=80,BLKSIZE=27920)
For variable-length:
123//SORTIN DD DSN=MY.VB.FILE,DISP=SHR //SORTOUT DD DSN=MY.VB.OUT,DISP=(NEW,CATLG), // SPACE=(CYL,(10,5)),DCB=(RECFM=VB,LRECL=256,BLKSIZE=27998)
If the dataset is cataloged, the catalog may supply RECFM and LRECL so you do not need DCB on the DD. For new datasets, always specify DCB to match what DFSORT will write.
If DFSORT cannot determine the record format from the DD or dataset (e.g. tape or dynamic allocation without RECFM/LRECL), use the RECORD control statement. RECORD TYPE=F and LENGTH=80 tell DFSORT fixed-length 80-byte records; RECORD TYPE=V and LENGTH=256 tell it variable-length with maximum 256 (including RDW). For normal disk datasets with a proper DCB, RECORD is usually not needed.
Imagine a row of mailboxes. With FB, every mailbox is the same size. We know exactly where each box starts and ends, and we never need to look at a label to see how big a box is. With VB, each mailbox can be a different size. Each one has a small label (the RDW) that says "I am this many bytes long." So we read the label, then read that many bytes. FB is like a spreadsheet where every row is the same length; VB is like a list of sentences where each sentence can be as short or long as it needs to be. We use FB when everything is the same size, and VB when sizes are different so we don't waste space.
1. What does the "B" in FB and VB stand for?
2. For RECFM=VB, what does LRECL represent?
3. When would you choose VB over FB for a DFSORT output?
4. In DFSORT control statements, does position 1 for a VB record include the RDW?
5. What RECFM would you use for an 80-byte card-image file where every record is 80 bytes?