Files
shellscripts/export_tex.sh
2026-03-24 20:23:08 +07:00

174 lines
6.6 KiB
Bash
Executable File

#!/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/"