#!/usr/bin/env bash # export_tex.sh — collect all files needed to compile a .tex file # Usage: ./export_tex.sh [manuscript.tex] [output_dir] # Defaults: first .tex in current dir; output_dir → ./export set -euo pipefail # ── arguments ──────────────────────────────────────────────────────────────── TEX_FILE="${1:-}" if [[ -z "$TEX_FILE" ]]; then TEX_FILE=$(ls *.tex 2>/dev/null | head -1) [[ -z "$TEX_FILE" ]] && { echo "No .tex file found."; exit 1; } fi [[ -f "$TEX_FILE" ]] || { echo "File not found: $TEX_FILE"; exit 1; } EXPORT_DIR="${2:-export}" SRC_DIR="$(dirname "$(realpath "$TEX_FILE")")" TEX_BASE="$(basename "$TEX_FILE")" mkdir -p "$EXPORT_DIR" IMAGE_EXTS=(".pdf" ".png" ".jpg" ".jpeg" ".eps" ".svg" ".PNG" ".JPG" ".JPEG") # Track already-processed tex files to avoid infinite loops declare -A VISITED_TEX # ── helpers ────────────────────────────────────────────────────────────────── copy_rel() { # Copy a file to EXPORT_DIR preserving its path relative to SRC_DIR. local src="$1" [[ -f "$src" ]] || { echo " [missing] $src"; return; } local rel="${src#$SRC_DIR/}" local dest="$EXPORT_DIR/$rel" mkdir -p "$(dirname "$dest")" cp -v "$src" "$dest" } find_recursive() { # Find file by basename+ext under SRC_DIR; prefer shallower paths. local base="$1"; shift local found="" for ext in "$@"; do # Sort by path depth (fewer slashes = shallower) then alphabetically found=$(find "$SRC_DIR" -name "${base}${ext}" \ | awk '{ print gsub(/\//,"/")" "$0 }' | sort -n | head -1 \ | cut -d' ' -f2-) [[ -n "$found" ]] && break done echo "$found" } copy_image() { local img="$1" # Normalise: strip leading ./ if present img="${img#./}" # 1. Try path as written in .tex (relative to SRC_DIR), with/without ext if [[ "$img" == *.* ]]; then local f="$SRC_DIR/$img" if [[ -f "$f" ]]; then copy_rel "$f"; return; fi else for ext in "${IMAGE_EXTS[@]}"; do local f="$SRC_DIR/${img}${ext}" if [[ -f "$f" ]]; then copy_rel "$f"; return; fi done fi # 2. Fallback: recursive search by basename only local base="${img##*/}" local f f=$(find_recursive "$base" "${IMAGE_EXTS[@]}") if [[ -n "$f" ]]; then copy_rel "$f" else echo " [missing] $img" fi } # ── recursive tex processor ─────────────────────────────────────────────────── process_tex() { local tex_path tex_path="$(realpath "$1")" [[ -n "${VISITED_TEX[$tex_path]:-}" ]] && return VISITED_TEX["$tex_path"]=1 [[ -f "$tex_path" ]] || { echo " [missing tex] $tex_path"; return; } copy_rel "$tex_path" # \input{file} or \include{file} while IFS= read -r inc; do # Try with and without .tex extension local candidate="" for try in "$SRC_DIR/${inc}.tex" "$SRC_DIR/${inc}"; do [[ -f "$try" ]] && { candidate="$try"; break; } done if [[ -n "$candidate" ]]; then process_tex "$candidate" else echo " [missing tex] $inc" fi done < <(grep -oP '\\(input|include)\{[^}]+\}' "$tex_path" \ | grep -oP '(?<=\{)[^}]+' | tr -d '\r') # \includegraphics while IFS= read -r img; do copy_image "$img" done < <(grep -oP '\\includegraphics(\[[^\]]*\])?\{[^}]+\}' "$tex_path" \ | grep -oP '(?<=\{)[^}]+' | tr -d '\r') } # ── 1. recurse through all .tex files ──────────────────────────────────────── process_tex "$TEX_FILE" # ── 2. document class (.cls) ────────────────────────────────────────────────── CLS=$(grep -oP '\\documentclass\[?[^\]]*\]?\{[^}]+\}' "$TEX_FILE" \ | grep -oP '(?<=\{)[^}]+' | tr -d '\r' | head -1) if [[ -n "$CLS" ]]; then f=$(find_recursive "$CLS" ".cls") if [[ -n "$f" ]]; then copy_rel "$f" else echo " [system] ${CLS}.cls (not local, assumed installed)" fi fi # Collect all files to search for bibliography settings: .tex + any local .cls/.sty mapfile -t SEARCH_FILES < <( printf '%s\n' "${!VISITED_TEX[@]}" find "$SRC_DIR" -maxdepth 1 \( -name "*.cls" -o -name "*.sty" \) 2>/dev/null ) # ── 3. bibliography style (.bst / .bbx) ────────────────────────────────────── BST=$(grep -rhP '\\bibliographystyle\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \ | grep -oP '(?<=\{)[^}]+' | tr -d '\r' | head -1 || true) if [[ -n "$BST" ]]; then f=$(find_recursive "$BST" ".bst" ".bbx") if [[ -n "$f" ]]; then copy_rel "$f" else echo " [system] ${BST}.bst (not local, assumed installed)" fi fi # also copy any local .bbx / .cbx referenced by biblatex style option # matches both style= and bibstyle= grep -rhP '\\usepackage.*biblatex' "${SEARCH_FILES[@]}" 2>/dev/null \ | grep -oP '(?:bib)?style\s*=\s*\{?[^},\]]+' | grep -oP '[^={} \r]+$' \ | tr -d '\r' | sort -u \ | while IFS= read -r sty; do for ext in ".bbx" ".cbx"; do f=$(find_recursive "$sty" "$ext") [[ -n "$f" ]] && copy_rel "$f" done done || true # ── 4. bibliography database (.bib) ────────────────────────────────────────── # handles both \bibliography{} (BibTeX) and \addbibresource{} (biblatex) while IFS= read -r bib; do bib="${bib%.bib}" # strip .bib suffix if already present f=$(find_recursive "$bib" ".bib") [[ -n "$f" ]] && copy_rel "$f" || echo " [missing] ${bib}.bib" done < <({ grep -rhP '\\bibliography\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \ | grep -oP '(?<=\{)[^}]+' | tr ',' '\n' | tr -d ' \r' grep -rhP '\\addbibresource\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \ | grep -oP '(?<=\{)[^}]+' | tr -d '\r'; } \ | sort -u || true) # ── 5. .bbl (pre-built bibliography, if present) ───────────────────────────── BBL="$SRC_DIR/${TEX_BASE%.tex}.bbl" [[ -f "$BBL" ]] && copy_rel "$BBL" echo "" echo "Done. Files exported to: $EXPORT_DIR/"