To extract a table from a PDF on the command line, run pdftotext with its layout option so the columns stay lined up as text, keep only the lines that are data rows, and split them into fields with awk. Write the fields out as CSV, and Excel opens the table with each value in its own column.
Command-line PDF tools that keep table layout
The command-line tool that keeps table layout best for most text PDFs is pdftotext, part of the Poppler utilities found on most Linux systems and installable on macOS and Windows. With its layout option it prints each page as plain text with the columns still lined up, which is the starting point for every step below.
Three tools cover most needs:
- pdftotext turns pages into text. It is fast, has no dependencies beyond Poppler, and is good for tables without ruling lines, such as transaction lists.
- tabula-java is a command-line build of the Tabula table detector. It looks for table areas itself and writes CSV, TSV or JSON directly. It needs Java.
- awk, cut and grep are standard text tools. They do the splitting and filtering once the PDF is text.
On Debian and Ubuntu, pdftotext comes in the poppler-utils package. On other systems, install Poppler from your usual package manager. Check that it works with pdftotext -v, which prints the version and exits. Version differences matter little for layout text, but if a colleague gets different spacing from the same file, compare versions before you compare anything else.
None of these read scanned pages. A scan is an image, and pdftotext returns nothing useful from it, so run OCR first or use a tool that includes it.
The command line suits repeated jobs and large batches. For a single small table, copying it from the PDF and repairing the paste in Excel is often faster than writing commands.
pdftotext -layout on a page range
pdftotext reads a page range with -f for the first page and -l for the last, and keeps the visual spacing with -layout. The pdftotext manual page says -layout maintains the original physical layout as best it can, while the default undoes columns and outputs text in reading order.
pdftotext -layout -f 3 -l 5 report.pdf table.txt
That writes pages 3 to 5 to table.txt. Giving - as the output name sends the text to standard output instead, so you can pipe it straight into the next command.
The other options worth knowing:
-nopgbrkstops pdftotext inserting a form-feed character between pages, which otherwise sticks to the first line of each new page.-x,-y,-Wand-Hcrop to a rectangle on the page. They help when the table shares the page with other text.-rawkeeps the content stream order. The manual says it is no longer recommended, and it usually scrambles columns.
Always look at the text before splitting it. less -S table.txt shows long lines without wrapping, so you can see whether the amounts line up under each other and which header and footer lines repeat on each page.
Splitting layout text into CSV columns with awk
Splitting layout text into CSV columns means picking out the data lines and cutting each one into fields. awk does both in one command: a pattern selects lines that start with a date, and a field separator of two or more spaces splits them, since a single space only appears inside a description.
A worked example
Here is a made-up page as pdftotext might print it:
Example Supply Co. Account activity
Date Description Amount
03/02/2026 Office chairs, order 4471 -1,240.00
03/05/2026 Customer payment "INV-208" 3,500.00
03/09/2026 Freight -86.50
Page 1 of 2
The command:
awk -F' +' 'BEGIN { OFS = "," }
/^[0-9][0-9]\/[0-9][0-9]\// {
gsub(/"/, "\"\"", $2)
gsub(/,/, "", $3)
print $1, "\"" $2 "\"", $3
}' table.txt > table.csv
And the CSV it writes:
03/02/2026,"Office chairs, order 4471",-1240.00
03/05/2026,"Customer payment ""INV-208""",3500.00
03/09/2026,"Freight",-86.50
Line by line, the command does this:
-F' +'sets the field separator to two or more spaces.- The pattern keeps only lines that start with two digits, a slash, two digits and a slash. The title, the header and the page footer fail it and disappear.
- The first
gsubdoubles any quote marks inside the description, which is how CSV escapes them. - The second
gsubremoves thousands commas from the amount, so Excel reads it as a number. printwrites the date, the description wrapped in quotes, and the amount.
Splitting by position instead
When a column can be empty, spacing alone cannot tell which column a value belongs to. If the layout puts each column at a fixed character position, cut by position: cut -c1-10 gives the date, and awk's substr($0, 13, 38) gives the description. Check the positions on the first and last page, since a layout can shift by a character or two.
If you only need one field, such as the amounts or the reference numbers, the job gets simpler. Extracting a single column from a PDF to Excel covers that case.
When is tabula-java the better command-line tool?
tabula-java is the better tool when a table has ruling lines between cells, when several tables share a page, or when columns are too uneven for spacing rules. It detects the table itself and writes CSV, TSV or JSON, so you skip the splitting step entirely. The trade-off is less control over each row.
The tabula-java README lists the options. A typical call:
java -jar tabula.jar --pages 3-5 --lattice --format CSV --outfile table.csv report.pdf
--pagestakes ranges orall. The default is page 1 only, which catches people out on multi-page tables.--latticeforces the mode for tables with ruling lines between cells.--streamforces the mode for tables without them.--arealimits extraction to a portion of the page, and can be repeated for several areas.--columnssets column boundaries by x coordinate, for when the detector splits a column wrongly.--guessasks it to guess the table area on each page.
Trying both on the same file
The quickest way to choose is to run both on one representative page and compare the two CSVs in Excel side by side. Look at three things. First, the row count: does each tool return the same number of data rows as the printed page? Second, the column count: did either tool split a description into two columns, or merge an amount into the text? Third, the junk: which output carries headers, footers or page numbers that you would have to delete by hand? The tool with fewer fixes on that page is usually the right one for the whole file, as long as the layout stays the same from page to page.
Use Tabula when it reads your layout correctly on the first try. If you find yourself setting areas and column positions for every file, pdftotext and awk may be simpler to maintain, because every rule is visible in a few lines of text.
Which command-line output breaks the CSV in Excel?
Command-line output breaks the CSV in Excel when a field contains the delimiter, when a row wraps onto two lines, or when Excel converts a value on opening. Commas in descriptions, quotes, long descriptions on two lines, and codes with leading zeros are the usual faults, and most are fixed in the awk step.
- Commas in descriptions. An unquoted comma splits one field into two. Quote every text field, as the example does.
- Quote marks. A quote inside a quoted field must be doubled. Without the
gsub, the row shifts. - Wrapped descriptions. A continuation line has no date, so the pattern drops it and the description loses its end. Handle it in awk by remembering the last row and appending any indented line with no amount before printing.
- Leading zeros. A reference such as
00412becomes 412 when Excel opens the CSV. Import the file with that column set to text instead of double-clicking it. - Dates. Excel reads
03/02/2026according to your regional settings, as either March 2 or February 3. Writing dates as2026-03-02in awk avoids the guess.
Subtotal lines are a quieter fault. They often start with a date or a group name and end with an amount, so they can pass the pattern and double the total. Handling a PDF table with subtotals shows how to flag and remove them.
A repeatable command-line script for PDF tables
A repeatable script chains the steps into one command you run on every new file: convert the page range, split the lines, write the CSV, and print a total to check against the PDF. Saved as a shell script, it turns a monthly job into a single line.
#!/bin/sh
# usage: ./pdf-table.sh report.pdf 3 5 > table.csv
pdftotext -layout -nopgbrk -f "$2" -l "$3" "$1" - |
awk -F' +' 'BEGIN { OFS = "," }
/^[0-9][0-9]\/[0-9][0-9]\// {
gsub(/"/, "\"\"", $2); gsub(/,/, "", $3)
print $1, "\"" $2 "\"", $3
total += $3; rows++
}
END { printf "rows %d, total %.2f\n", rows, total > "/dev/stderr" }'
The CSV goes to standard output and the check line goes to standard error, so the check appears on screen while the file stays clean. Compare the row count and total with the PDF each time. A total that matches is the best evidence the rules still fit the layout. One that does not is the signal to look at the text again before anyone uses the file.
Run the script on a second month before trusting it. Layouts drift, and a new kind of line, such as a fee printed in a separate box, will be silently skipped. The way converters of every kind get measured against the source is laid out in how accurate PDF to Excel converters are, and the same tests apply to your own script.
For bank statements, FastBankConvert is meant to replace this pipeline: you drop in the PDF and it checks the balances before you download. It is not open yet, and the get-started page collects your email for the launch.