# pipeline syntax Chaining operations, named handles, and pipeline substitution ## Details Construct complex workflows using pipelines, handles, and substitution. **1. Chaining (`---`)** Multiple operations can be chained together using `---`. The output of one stage becomes the input for the next stage. If a stage has no explicit inputs, it automatically uses the result from the previous stage. ``` pdftl in.pdf rotate right --- crop '(a4)' output out.pdf ``` You can have as many stages as you like. Each `---` separator marks the boundary between stages. **2. Named Handles (`X=...`)** You can assign single capital letter handles (A-Z) to inputs to refer to them later: ``` pdftl A=logo.pdf B=content.pdf ... ``` This allows you to reuse a specific file or result multiple times in different stages (e.g., `cat A B A`). Handles are visible to all subsequent stages in the same pipeline. **3. Pipeline Substitution (`JOB ... DONE`)** Similar to command substitution in a shell (`$(...)` in bash), you can process files in a temporary sub-pipeline and use the result as an input — inline, without saving to a file first. ``` pdftl JOB in.pdf rotate right DONE main.pdf cat output out.pdf ``` The commands between `JOB` and `DONE` run first, and their output is used as an input at that position. You can also assign the result to a named handle: ``` pdftl S=JOB in.pdf rotate right DONE S main.pdf cat output out.pdf ``` Sub-pipelines can themselves contain `---` separators to chain operations inside the substitution: ``` pdftl A=JOB in.pdf rotate right --- stamp logo.pdf DONE ... ``` Sub-pipelines can be nested arbitrarily. Each `DONE` always closes the innermost open `JOB`. **4. Per-file Iteration (`EACH ... DONE`)** `EACH ... DONE` applies a sub-pipeline to every input file that precedes it, independently, replacing each input with its transformed result. This is similar to `map` in functional programming, or `xargs` in the shell. ``` pdftl a.pdf b.pdf c.pdf EACH rotate right DONE cat output out.pdf ``` Here, `rotate right` is applied to each of `a.pdf`, `b.pdf`, and `c.pdf` in turn. The results are then passed to `cat` for merging. The sub-pipeline inside `EACH ... DONE` receives each input file via the implicit pipeline input `_`, exactly as if it were a stage after `---`. This means you can chain multiple operations inside `EACH`: ``` pdftl a.pdf b.pdf EACH cat 1-3 --- rotate right DONE cat output out.pdf ``` This selects pages 1–3 from each file, rotates them, and merges the results. **`EACH` and filename substitution** Because each iteration runs as an isolated sub-pipeline with one input file, operations that substitute the current filename (such as `add_text`) work naturally inside `EACH`: ``` pdftl *.pdf EACH insert before 1 --- add_text '/{filename}/(position=center)' DONE cat output combined.pdf ``` This inserts a blank title page stamped with each file's own name before merging everything together. **5. Combining `JOB` and `EACH`** `JOB` and `EACH` can be freely combined. Both use `DONE` as their terminator; `DONE` always closes the innermost open block. *`EACH` inside `JOB`*: the `JOB` collects the results of all iterations into a single output: ``` pdftl R=JOB a.pdf b.pdf EACH cat 1 DONE DONE cat R output out.pdf ``` `EACH` selects page 1 from each of `a.pdf` and `b.pdf`; the enclosing `JOB` merges them and assigns the 2-page result to `R`. *`JOB` inside `EACH`*: each iteration can use its own sub-pipeline as an additional input. Pass `_` explicitly to the `JOB` to refer to the current iteration's file: ``` pdftl a.pdf b.pdf EACH R=JOB _ cat 1 DONE cat R _ DONE cat output out.pdf ``` For each input file, `JOB` extracts its first page into `R`, then `cat R _` prepends that first page to the full file. *Siblings*: `EACH` and `JOB` can appear as siblings in the same input list. Each gets its own `DONE`: ``` pdftl a.pdf b.pdf EACH cat 1 DONE R=JOB cover.pdf stamp logo.pdf DONE cat R output out.pdf ``` The two `DONE` tokens close `EACH` and `JOB` respectively. The outer `cat` receives: the `EACH` results (one page from each of `a` and `b`) followed by `R` (the stamped cover), and merges all three. ## Examples > Shuffle two documents, then crop the resulting pages to A4 ``` pdftl a.pdf b.pdf shuffle --- crop '(a4)' output out.pdf ``` > Crop all pages to A3 in landscape, and preview the effect of cropping odd pages to A4 ``` pdftl in.pdf crop '(A3_l)' --- crop 'odd(A4)' preview output out.pdf ``` > Save a snapshot of a rotated file, then apply a stamp and save the final version ``` pdftl in.pdf rotate right output rotated_snapshot.pdf --- background watermark.pdf output final.pdf ``` > Use pipeline substitution (JOB...DONE) to rotate one filebefore merging it with another. ``` pdftl JOB in.pdf cat right DONE main.pdf cat output final.pdf ``` > Rotate and stamp a.pdf, crop b.pdf, then combine selected pages from both ``` pdftl A=JOB a.pdf rotate right --- stamp logo.pdf DONE B=JOB b.pdf crop '(a4)' DONE cat A1-3 B2-end output combined.pdf ``` > Join a contract with a stamped copy of itself ``` pdftl contract.pdf JOB contract.pdf stamp logo.pdf DONE output combined.pdf ``` > Chain multiple operations together where the output of one becomes the input of the next ``` pdftl in.pdf rotate right --- crop '(a4)' output out.pdf ``` > Assign named handles to inputs to reuse them later in the pipeline ``` pdftl A=logo.pdf B=content.pdf cat A B A output out.pdf ``` > Assign the result of a sub-pipeline to a named handle ``` pdftl S=JOB in.pdf rotate right DONE S main.pdf cat output out.pdf ``` > Chain multiple operations inside a sub-pipeline ``` pdftl A=JOB in.pdf rotate right --- stamp logo.pdf DONE cat A output out.pdf ``` > Apply a sub-pipeline to multiple files independently replacing each with its transformed result ``` pdftl a.pdf b.pdf c.pdf EACH rotate right DONE cat output out.pdf ``` > Chain multiple operations inside an EACH block (e.g., select pages, then rotate) ``` pdftl a.pdf b.pdf EACH cat 1-3 --- rotate right DONE cat output out.pdf ``` > Use filename substitution inside an EACH block to stamp each file with its own name ``` pdftl a.pdf b.pdf EACH insert before 1 --- add_text '/{filename}/(position=center)' DONE cat output combined.pdf ``` > Wrap EACH inside a JOB to collect the results of all iterations into a single handle ``` pdftl R=JOB a.pdf b.pdf EACH cat 1 DONE DONE cat R output out.pdf ``` > Use a JOB inside EACH to process a specific page and prepend it to the current iteration's file ``` pdftl a.pdf b.pdf EACH R=JOB _ cat 1 DONE cat R _ DONE cat output out.pdf ``` > Combine EACH and JOB as siblings in the same pipeline ``` pdftl a.pdf b.pdf EACH cat 1 DONE R=JOB cover.pdf stamp logo.pdf DONE cat R output out.pdf ``` *Source: pdftl.cli.pipeline* *Read online: [https://pdftl.readthedocs.io/en/stable/general/pipeline.html](https://pdftl.readthedocs.io/en/stable/general/pipeline.html)* *Type: HelpTopic*