The last extra argument
letter.sh, the cover-letter pipeline, ends by assembling PDFs. Its calling convention has four branches, and only the extended form uses an inputs-then-destination tail. From the header comments:
./letter.sh # Output to terminal
./letter.sh cover.pdf # Create PDF
./letter.sh merge.pdf resume.pdf # Merge with other documents
./letter.sh cover.pdf resume.pdf [...] merge.pdfWith one argument, the script writes the cover there. With three or more, the first path receives a standalone cover, the final path receives the full bundle, and the paths between are additional inputs.
That last branch reads sources then target—the same shape as cp and mv. pdfcpu’s own grammar is the reverse: pdfcpu merge OUT IN IN... puts the destination first. The bridge between the two conventions is Bash array slicing:
local output=${1-}
local extras=( "${@:2}" )
...
pdfcpu merge -q \
"${extras[@]: -1}" \
"$CONTEXT_AT/cov.pdf" \
"${extras[@]: 0: ${#extras[@]}-1}""${extras[@]: -1}" is the last extra element—the extended form’s destination, moved into pdfcpu’s first slot. "${extras[@]: 0: ${#extras[@]}-1}" is every preceding extra—the input PDFs. The freshly generated cover is spliced between destination and inputs so it lands first in the merged document.
The gotcha is the space, and it is required. "${extras[@]: -1}" with the space is negative-offset slicing: the last element. Without it, "${extras[@]:-1}" is a different operator entirely—use-default-value, which expands to the string 1 when the array is empty and the array’s contents otherwise. The bash manual is explicit that a negative offset must be separated from the colon by at least one space to avoid being confused with :-. Same characters, one space, two unrelated features; the length slice needs no such care because a digit after the colon can’t be mistaken for a default.
The two-argument branch had a separate defect. It intended to merge the second PDF into the first destination, but copied the cover there and then ran pdfcpu merge -q "$output" "${extras[@]}". In pdfcpu 0.9.1, current when this script was committed, default create mode could overwrite an existing output. Because the cover was not an input, the command replaced it with the resume. That branch needed -mode append.
The whole document-production tail is this plus one direct pandoc call (--variable=pagestyle:empty, so the cover page carries no header or footer)—no custom LaTeX templates and no PDF libraries of my own. Pandoc delegates the typesetting to an external PDF engine, pdflatex by default, so that engine is a third executable dependency. The historical check:deps checks for pandoc and pdfcpu but not the engine.