Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I've got a double-side-printed multipage document to scan with a linear bulk scanner. So, as the result I get 2 PDF files: one containing all odd pages and the second containing all even pages. I need to merge them the natural way:

1. <- 1.1. (odd.pdf page 1 to result.pdf page 1)
2. <- 2.1. (even.pdf page 1 to result.pdf page 2)
3. <- 1.2. (odd.pdf page 2 to result.pdf page 3)
4. <- 2.2. (even.pdf page 2 to result.pdf page 4)


share|improve this question
Just find a PDF parser and do a merge sort like stuff. –  warl0ck Oct 30 '12 at 10:00
Thanks, captain Obvious! :-] –  Ivan Oct 30 '12 at 10:02
If Stephane doesn't solve your problem, you can try the perl module CAM::PDF, I'll give your a script later. Does the two pdf have same page count? –  warl0ck Oct 30 '12 at 11:24

5 Answers 5

up vote 4 down vote accepted

See the pdfseparate and pdfunite commands from poppler-utils. The first to separate the pages from each document into individual files, and the second to merge them in the order you want in a new document.

Also note that since scanners give you raster images anyway (which some like yours can concatenate into a PDF files), maybe you can configure it to output images (png, tiff...) instead, and do the concatenation into a PDF yourself with ImageMagick.

share|improve this answer
This sounds like what I need, let's try... –  Ivan Oct 30 '12 at 10:07
Indeed. Excellent. Simple to use to do right what I need. by the way, I have, of course, googled before asking and solutions I've found to exactly the same were notably more complex. –  Ivan Oct 30 '12 at 13:07

Just a bash quick shot using pdfjam:

Build an array of input arguments:

for k in $(seq 1 ${N_PAGES}); do

This should allow you to use it as input list for pdfjoin:

 pdfjoin ${PAGES[@]} --outfile shuffled.pdf
share|improve this answer
It should be noted that pdfjoin is a wrapper script around pdfjam which is itself a wrapper script around the pdfpages LaTeX package (and pdflatex) so it means that it brings LaTeX as a dependency. –  Stéphane Chazelas Oct 30 '12 at 14:11

pdftk has a shuffle command which collates pages:

pdftk A=even.pdf B=odd.pdf shuffle A B output collated.pdf
share|improve this answer

I was looking to do basically the same thing, and Stéphane Chazelas's answer was very helpful. I do this often enough that I wrote a simple Python script to automate things, using the commands he suggested. By default, it reverses the order of the even pages, but this can be suppressed with a command line flag.

The question is kind of old, so I expect the needs of the original asker have already been met. However, it is possible that the script will be useful to people who arrive here in the future, so I have placed it below.

"""A simple script to merge two PDFs."""

import argparse
from os import listdir
from os.path import join as opjoin
import shutil
from subprocess import check_call, CalledProcessError
import tempfile

SEPARATE = 'pdfseparate %s %s'
MERGE = 'pdfunite %s %s'

def my_exec(command):
    """Execute a command from a shell, ignoring errors."""
        check_call(command, shell=True)
    except CalledProcessError:

def run(odd, even, out, reverse_odd=False, reverse_even=True):
    """Interleave odd and even pages from two PDF files."""
    folder = tempfile.mkdtemp()
    my_exec(SEPARATE % (odd, opjoin(folder, 'odd%d.pdf')))
    my_exec(SEPARATE % (even, opjoin(folder, 'even%d.pdf')))
    odd_files = []
    even_files = []
    for curr_file in listdir(folder):
        filepath = opjoin(folder, curr_file)
        if curr_file.startswith('odd'):
            odd_files.append((filepath, int(curr_file[3:-4])))
        elif curr_file.startswith('even'):
            even_files.append((filepath, int(curr_file[4:-4])))
    func = lambda x: x[1]
    odd_files.sort(key=func, reverse=reverse_odd)
    even_files.sort(key=func, reverse=reverse_even)
    parts = []
    for line in zip(odd_files, even_files):
    my_exec(MERGE % (' '.join(parts), out))

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Merge two PDF files.')
    parser.add_argument('odd_pages', help='PDF containing the odd pages.')
    parser.add_argument('even_pages', help='PDF containing the even pages.')
    parser.add_argument('output_file', help='The target output file.')
    parser.add_argument('--reverse-odd', action='store_true', 
                        help='Insert the odd pages in reverse order.')
    parser.add_argument('--no-reverse-even', action='store_true',
                        help='Suppress reversal of the even pages.')
    args = parser.parse_args()
    run(args.odd_pages, args.even_pages, args.output_file,
        args.reverse_odd, not args.no_reverse_even)
share|improve this answer

I came across this bash script doing this, it assumes you scanned the even pages in reverse order, but you can change this removing the -r in the line saying evenpages=($(ls "$evenbase-$key-"* | sort -r)) (this is line 46)

# Copyright Fabien André <>
# Distributed under the MIT license
# This script interleaves pages from two distinct PDF files and produces an
# output PDF file. The odd pages are taken from a first PDF file and the even
# pages are taken from a second PDF file passed respectively as first and second
# argument.
# The first two pages of the output file are the first page of the
# odd pages PDF file and the *last* page of the even pages PDF file. The two
# following pages are the second page of the odd pages PDF file and the
# second to last page of the even pages PDF file and so on.
# This is useful if you have two-sided documents scanned each side on a
# different file as it can happen when using a one-sided Automatic Document
# Feeder (ADF)
# It does a similar job to :
# but only requires bash (> 4.0) and poppler utils.
# Print usage/help message
function usage {
echo "Usage: $0 <PDF-even-pages-file> <PDF-odd-pages-file>"
exit 1
# Add leading zeros to pad numbers in filenames matching the pattern
# $prefix$number.pdf. This allows filenames to be easily sorted using
# sort.
# $1 : The prefix of the filenames to consider
function add_leading_zero {
baseprefix=$(basename $prefix | sed -e 's/[]\/()$*.^|[]/\\&/g')
dirprefix=$(dirname $prefix)
for filename in "$prefix"*".pdf"
base=$(basename "$filename")
index=$(echo "$base" | sed -rn "s/$baseprefix([0-9]+).pdf$/\1/p")
newbase=$(printf "$baseprefix%04d.pdf" $index)
mv $filename "$dirprefix/$newbase"
# Interleave pages from two distinct PDF files and produce an output PDF file.
# Note that the pages from the even pages file (second file) will be used in
# the reverse order (last page first).
# $1 : Odd pages filename
# $2 : Odd pages filename with extension removed
# $3 : Even pages filename
# $4 : Even pages filename with extension removed
# $5 : Unique key used for temporary files
# $6 : Output file
function pdfinterleave {
# Odd pages
pdfseparate $oddfile "$oddbase-$key-%d.pdf"
add_leading_zero "$oddbase-$key-"
oddpages=($(ls "$oddbase-$key-"* | sort))
# Even pages
pdfseparate $evenfile "$evenbase-$key-%d.pdf"
add_leading_zero "$evenbase-$key-"
evenpages=($(ls "$evenbase-$key-"* | sort -r))
# Interleave pages
pdfunite ${pages[@]} "$outfile"
rm ${oddpages[@]}
rm ${evenpages[@]}
if [ $# -lt 2 ]
if [ $1 == $2 ]
echo "Odd pages file and even pages file must be different." >&2
exit 1
if ! hash pdfunite 2>/dev/null || ! hash pdfseparate 2>/dev/null
echo "This script requires pdfunite and pdfseparate from poppler utils" \
"to be in the PATH. On Debian based systems, they are found in the" \
"poppler-utils package"
exit 1
odddir=$(dirname $oddbase)
oddfile=$(basename $oddbase)
evenfile=$(basename $evenbase)
key=$(tr -dc "[:alpha:]" < /dev/urandom | head -c 8)
if [ -e $outfile ]
echo "Output file $outfile already exists" >&2
exit 1
pdfinterleave $1 $oddbase $2 $evenbase $key $outfile
# SO - Bash command that prints a message on stderr
# SO - Check if a program exists from a bash script
# SO - How to debug a bash script?
# SO - Escape a string for sed search pattern


share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.