- Posted on
- • Artificial Intelligence
Artificial Intelligence Document Summarisation
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
AI Document Summarisation on Linux: Turn Document Piles into Useful Briefs from the Command Line
Drowning in PDFs, reports, and meeting notes? Most of us are. The value is obvious: less time reading, more time deciding. In this post, you’ll learn how to summarize documents from the Linux command line using open‑source tools—extractive and abstractive—keeping your data local and automating the workflow with Bash.
What you’ll get:
Why AI summarisation is worth doing locally
A minimal, reproducible toolchain you can install with apt, dnf, or zypper
3–5 actionable steps with real commands
A ready‑to‑use Python summariser script for long documents
A simple Bash wrapper to glue it all together
Why summarise locally?
Privacy and compliance: Summarise sensitive documents without sending them to third‑party services.
Cost and speed: Batch many files with predictable, zero‑API costs.
Automation: Unix pipes and scripts make it easy to plug summarisation into existing workflows.
Choice: Use extractive (quotes sentences) or abstractive (rewrites) summaries depending on your need for fidelity vs. readability.
Extractive vs. Abstractive:
Extractive: Picks key sentences from the original. Fast, faithful, but sometimes choppy.
Abstractive: Generates new wording, like a human summary. More readable, but requires a model.
1) Install prerequisites
We’ll need tools to:
Convert documents to plain text:
pdftotext,pandoc,lynxRun Python models: Python 3, virtual environments, and pip
CLI helpers:
curl,jq(optional)Optional OCR for scanned PDFs:
tesseract
Pick the commands for your distro.
Ubuntu/Debian (apt):
sudo apt update
sudo apt install -y \
python3 python3-venv python3-pip git curl jq \
pandoc poppler-utils lynx \
tesseract-ocr
Fedora/RHEL (dnf):
sudo dnf install -y \
python3 python3-pip python3-virtualenv git curl jq \
pandoc poppler-utils lynx \
tesseract
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y \
python3 python3-pip python3-virtualenv git curl jq \
pandoc poppler-tools lynx \
tesseract
Create a clean Python environment for AI models:
python3 -m venv ~/.venvs/ai-sum && source ~/.venvs/ai-sum/bin/activate
pip install --upgrade pip
For extractive and abstractive summarisation:
# Extractive CLI
pip install sumy
# Abstractive models
pip install "transformers>=4.40" torch sentencepiece
Tip: If you have an NVIDIA GPU and want acceleration, install the CUDA‑enabled PyTorch wheel from pytorch.org’s “Get Started” page, then rerun the abstractive steps.
2) Normalize documents to plain text
Plain text in, summary out. Use these quick conversions:
- PDF to text (non‑scanned):
pdftotext -layout input.pdf output.txt
- PDF (scanned) to text via OCR:
pdftotext input.pdf - | wc -c | grep -qv '^0$' || \
(echo "OCRing scanned PDF..." >&2; pdftoppm input.pdf -png | \
while read -r img; do tesseract "$img" stdout; done) > output.txt
- DOCX to text:
pandoc -s input.docx -t plain -o output.txt
- HTML to text:
lynx -dump -nolist input.html > output.txt
- Markdown to text:
pandoc -s input.md -t plain -o output.txt
Quick cleanup (optional):
sed -i 's/[[:space:]]\+/ /g; s/[[:cntrl:]]//g' output.txt
3) Fast extractive summaries with Sumy (no ML weights)
When you want speed and faithfulness, use Sumy’s LexRank:
Summarise a file to 7 sentences:
sumy lex-rank --length=7 --language=en --file=output.txt
Summarise a URL directly:
sumy lex-rank --length=7 --language=en --url="https://example.com/article"
Batch over a directory:
find docs/ -type f -name "*.txt" -print0 | while IFS= read -r -d '' f; do
echo "## $(basename "$f")"
sumy lex-rank --length=5 --language=en --file="$f"
echo
done
Pros: Tiny footprint, fast, deterministic.
Cons: Can be choppy; won’t rewrite awkward prose.
4) Abstractive AI summaries with Transformers (local model)
The following script handles long documents by chunking them and re‑summarising the partial results.
Create summarize_ai.py:
cat > summarize_ai.py <<'PY'
#!/usr/bin/env python3
import os, sys, re, argparse
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, pipeline
import torch
def chunk_sentences(text, tokenizer, max_input_tokens=900):
# naive sentence split; good enough for general English prose
sentences = re.split(r'(?<=[.!?])\s+', text.strip())
chunks, buf = [], []
for s in sentences:
if not s: continue
buf.append(s)
toks = len(tokenizer.encode(" ".join(buf), add_special_tokens=False))
if toks >= max_input_tokens:
last = buf.pop()
chunks.append(" ".join(buf).strip())
buf = [last]
if buf:
chunks.append(" ".join(buf).strip())
return [c for c in chunks if c]
def main():
ap = argparse.ArgumentParser(description="Local abstractive summariser")
ap.add_argument("-f", "--file", help="Input file (default: stdin)")
ap.add_argument("--model", default=os.environ.get("SUM_MODEL", "sshleifer/distilbart-cnn-12-6"),
help="HF model id (default: distilbart-cnn)")
ap.add_argument("--min-tokens", type=int, default=50, help="Min tokens per summary")
ap.add_argument("--max-tokens", type=int, default=200, help="Max tokens per summary")
ap.add_argument("--max-input-tokens", type=int, default=900, help="Max tokens per chunk")
args = ap.parse_args()
text = open(args.file, "r", encoding="utf-8").read() if args.file else sys.stdin.read()
if not text.strip():
print("No input text.", file=sys.stderr)
sys.exit(1)
tokenizer = AutoTokenizer.from_pretrained(args.model, use_fast=True)
model = AutoModelForSeq2SeqLM.from_pretrained(args.model)
device = 0 if torch.cuda.is_available() else -1
summariser = pipeline("summarization", model=model, tokenizer=tokenizer, device=device)
chunks = chunk_sentences(text, tokenizer, max_input_tokens=args.max_input_tokens)
partials = []
for ch in chunks:
out = summariser(ch, max_length=args.max_tokens, min_length=args.min_tokens, do_sample=False)[0]["summary_text"]
partials.append(out)
summary = " ".join(partials)
# Second-pass summary if we had to chunk
if len(partials) > 1:
summary = summariser(summary, max_length=args.max_tokens, min_length=args.min_tokens, do_sample=False)[0]["summary_text"]
print(summary.strip())
if __name__ == "__main__":
main()
PY
chmod +x summarize_ai.py
Usage examples:
# Summarise a text file to ~200 tokens
./summarize_ai.py -f output.txt --max-tokens 200 --min-tokens 60
# Use a different model (heavier, often better)
SUM_MODEL=facebook/bart-large-cnn ./summarize_ai.py -f output.txt
# Pipe directly from converters
pdftotext -layout report.pdf - | ./summarize_ai.py --max-tokens 220
Notes:
sshleifer/distilbart-cnn-12-6is smaller/faster;facebook/bart-large-cnnis larger/more fluent.For very long docs, increase
--max-input-tokenscarefully (model limits apply, typically 1024 tokens input).
5) Glue it together with a single Bash command
This function picks the right extractor (pdf/docx/html/txt), then runs either extractive (fast) or abstractive (AI) summarisation. Drop it into your shell profile.
summarize() {
local mode="${1:-ai}" # ai|fast
local in="$2"
[ -z "$in" ] && { echo "Usage: summarize [ai|fast] <file>"; return 1; }
# Convert to plaintext on stdout
case "$in" in
*.pdf)
if pdftotext "$in" - >/dev/null 2>&1; then pdftotext -layout "$in" -; else
echo "PDF appears scanned; using OCR..." >&2
pdftoppm "$in" -png | while read -r img; do tesseract "$img" stdout; done
fi
;;
*.docx) pandoc -s "$in" -t plain - ;;
*.html|*.htm) lynx -dump -nolist "$in" ;;
*.md) pandoc -s "$in" -t plain - ;;
*.txt) cat "$in" ;;
*) echo "Unsupported type: $in" >&2; return 2 ;;
esac | {
if [ "$mode" = "fast" ]; then
sumy lex-rank --length=7 --language=en --file=-
else
./summarize_ai.py --max-tokens 220 --min-tokens 60
fi
}
}
Examples:
# Fast extractive summary (7 sentences)
summarize fast notes.txt
# Abstractive AI summary of a PDF
summarize ai report.pdf > report.tldr.txt
# Batch summarise a folder of PDFs into .tldr files (AI mode)
find ./inbox -type f -name "*.pdf" -print0 | while IFS= read -r -d '' f; do
summarize ai "$f" > "${f%.*}.tldr.txt"
done
Real‑world mini‑pipeline example
Input: Quarterly results PDF
Output: 1‑page TL;DR + JSON metadata
pdftotext -layout Q2_results.pdf - | tee Q2_results.txt | ./summarize_ai.py --max-tokens 240 > Q2_results.tldr.txt
jq -n \
--arg file "Q2_results.pdf" \
--arg summary "$(cat Q2_results.tldr.txt)" \
--arg date "$(date -Iseconds)" \
'{file:$file, generated:$date, summary:$summary}' \
> Q2_results.summary.json
Store, search, and share your summaries with any text tools you already use.
Tips for quality and speed
Start “fast” (extractive) for triage; re‑run “ai” for important docs.
Control length with
--max-tokensand--min-tokens.Keep context: If critical details are consistently dropped, reduce chunk size so the model sees tighter sections.
Hardware matters: CPU works; GPU is faster for large batches.
Conclusion and next steps
You now have a practical, local, scriptable workflow to summarise PDFs, DOCX, HTML, and more—right from Bash. Try it on your document backlog, then:
Wire it into a daily cron job to summarise new files in a folder.
Add a mail filter to summarise long attachments automatically.
Experiment with different models (
facebook/bart-large-cnn,google/pegasus-xsum) for your domain.
If you found this useful, save the scripts, share the post, and iterate on the Bash function for your stack. Your future self (and your attention span) will thank you.