How-to2026-09-2910 min läsning

pdfx i terminalen och i CI: från första kommandot till ett pålitligt skript

Använd pdfx på kommandoraden för att slå ihop, komprimera och lägga till vattenmärken, bearbeta många filer på en gång och kedja verktyg i en enda begäran med en pipeline. För skript och CI: vad du kan lita på, nämligen slutkoder, rapporten --json, standard in och ut, och varför ett villkorsfilterverktyg utan träff ändå avslutas med 0.

PDF123 · Updated 2026-09-29

Innan du lägger pdfx i ett skript eller i kontinuerlig integration (CI), kom ihåg två saker. Slutkod 0 betyder inte alltid att en fil skrevs: ett villkorsfilterverktyg utan träff avslutas också med 0. Och --json har olika form för en indatafil och för flera, så ett skript som bara tolkar arrayen files hittar ingenting när bara en fil fick träff. pdfx är en HTTP-klient: filerna laddas upp till servern för bearbetning, det finns ingen lokal motor och inget offlineläge. Hela kommandoreferensen finns i CLI-guiden på utvecklarsidan.

Diagram: ett pdfx-anrop ger skriptet tre saker, en JSON-rapport på standard ut, felmeddelanden på standard fel och en slutkod; skriptet läser slutkoden först

Installera och slå sedan ihop två filer

Du behöver Node 20.3 eller nyare, eller Bun:

npm install -g @pdf123/cli
pdfx --version

Om du hellre inte installerar globalt sätter du npx @pdf123/cli framför kommandot. Med två PDF-filer i den aktuella katalogen:

pdfx merge a.pdf b.pdf -o merged.pdf

Vid lyckat resultat skriver standard ut merged.pdf. Det var Slå ihop PDF. Innan du byter verktyg, be om fälten i stället för att gissa parameternamn:

pdfx describe watermark
Add Watermark (watermark) - Add text or image watermarks to PDF files
Input:    1 file (.pdf)
Result:   file
Fields:
  --watermarkText <value>  Watermark text [default: PDF123]
  --fontSize <number>      Font size [default: 30]
                           min 6, max 200

(Ett utdrag; fälten omfattar också --rotation, --customColor och fler.) Ett fält är bara ett kommandoradsalternativ:

pdfx watermark in.pdf --watermarkText DRAFT -o marked.pdf

pdfx list listar alla 95 verktyg, --category security listar bara en kategori och --query watermark söker på ord.

Flera filer hamnar i en katalog, och befintliga filer skrivs inte över

Ge ett enfilsverktyg flera indatafiler så skrivs varje fil till katalogen som -o pekar ut så fort den är klar, inte först på slutet. Om en fil misslyckas går de andra vidare, och slutkoden i slutet av batchen är 1.

pdfx compress a.pdf b.pdf -o small/

Standard ut från en körning såg ut så här, och ordningen kan skilja sig varje gång:

a.pdf -> small/a.pdf
b.pdf -> small/b.pdf

En befintlig katalog fungerar som den är; om katalogen inte finns än behöver -o ett avslutande /, annars tolkas small som ett filnamn. Utan -o hamnar resultaten i den aktuella katalogen under det namn servern ger dem; en befintlig fil skrivs inte över och den nya blir a-1.pdf, a-2.pdf. Ett uttryckligt filnamn (-o same.pdf) ersätter befintligt innehåll, som du bad om. Som standard bearbetas två filer åt gången; ändra det med --concurrency. Trycker du Ctrl-C mitt i en batch finns de redan skrivna filerna kvar och slutkoden är 130.

Öppna en krypterad fil med --input-password LÖSENORD. När en batch blandar låsta och olåsta filer använder du --password-for FIL=LÖSENORD, som kan upprepas.

Anropa verktygen var för sig för mellanfiler, annars använd en pipeline

För att slå ihop, sedan lägga på vattenmärke och sedan komprimera, när du inte behöver mellanresultaten på disk, vik ihop allt till en begäran med pipeline; mellanfilerna laddas inte ner och upp igen. Vill du ha filen från varje steg anropar du verktygen var för sig. En pipeline ger en enda fil.

pdfx pipeline a.pdf b.pdf --step merge --step compress -o merged-small.pdf

# När ett steg behöver parametrar, beskriv stegen i JSON
pdfx pipeline a.pdf b.pdf \
  --steps '[{"tool":"merge"},{"tool":"watermark","params":{"watermarkText":"DRAFT"}},{"tool":"compress"}]' \
  -o out.pdf

En pipeline har högst 8 steg; ett 9:e steg får HTTP 400: at most 8 pipeline steps allowed. Ett verktyg som behöver en andra fil (till exempel overlay-pdfs, som lägger en annan PDF ovanpå) kan inte vara ett pipeline-steg. pdfx avvisar det före uppladdningen, med slutkod 2 och meddelandet Tool "overlay-pdfs" needs a second file and cannot run as a pipeline step.

Använd standard in bara när du vill koppla kommandot till en pipe. Ett filargument - läser från standard in, och -o - skriver resultatet till standard ut:

cat report.pdf | pdfx compress - -o - > report-small.pdf

I det här läget bär standard ut bara filens bytes; meddelanden och fel går till standard fel, så omdirigering är säker. Vi kontrollerade det med en PDF på cirka 1 KB: standard ut innehöll en PDF på 1040 byte, lika stor som filen som skrevs med -o, och standard fel var tomt. När du läser från standard in och inte anger -o får resultatet namnet stdin.pdf, skrivs till den aktuella katalogen och en notering skrivs på standard fel.

Skript ska matcha orsaken, inte meddelandet

Slutkod Betydelse Exempel
0 Lyckat, eller ett villkorsfilterverktyg utan träff En sammanslagning lyckades; villkoret i filter-page-count var falskt
1 Begäran skickades men misslyckades; i en batch misslyckades minst en fil Fel lösenord, ingen PDF, timeout, inget att returnera
2 Användningsfel, inget laddades upp Felstavat verktygsnamn, ett pipeline-steg som behöver en andra fil, en resultattyp som strider mot utdatafilens ändelse
130 Du avbröt Ctrl-C under en batch

Vid fel kommer, förutom en rad meddelande, tre rader på standard fel: reason:, code: och hint:. En krypterad fil med fel lösenord gav detta lokalt:

pdfx: HTTP 400: The password is incorrect.
reason: wrong_password
code: bad_request
hint: Check the password and try again.

Hur du skickar lösenordet ändrar meddelandets första rad: unlock --password ger raden ovan. Med --input-password på ett annat verktyg behandlar servern upplåsningen som ett internt steg och första raden är HTTP 400: Pipeline step 0 (security/remove-password) failed: The password is incorrect., medan raden reason: är wrong_password i båda fallen. Utan något lösenord alls är meddelandet This PDF is password-protected. Enter its password. och reason är password_required. Ett skript bör matcha raden reason:. code är grövre, och ett vanligt värde är bad_request.

Ett felstavat verktygsnamn avslutas med 2, och meddelandet föreslår liknande namn:

pdfx: Unknown tool "compres". Did you mean: compress, decompress-pdf? Run `pdfx list` to see all tools.

En resultattyp som strider mot filnamnet avslutas också med 2, och det sker innan något skrivs. Att skriva ZIP-filen som Dela PDF ger till x.pdf:

pdfx: The result is application/zip but "x.pdf" has a .pdf extension; name it *.zip or write into a directory
code: output_mismatch

En timeout avslutas också med 1, med code satt till timeout. Enheten för --timeout är millisekunder och standardvärdet är 300000, alltså 5 minuter, så --timeout 60 betyder 60 millisekunder, inte 60 sekunder.

När villkoret är falskt är slutkoden ändå 0

En klass av verktyg svarar på en ja-eller-nej-fråga: är sidantalet större än N, innehåller filen viss text, är filen större än en viss storlek. När svaret är ja returnerar de indatafilen oförändrad; när det är nej returnerar de ingenting, och pdfx skriver en rad no match och avslutas med 0. De här filterverktygen finns bara i SDK:t, på kommandoraden och i MCP; webbplatsen har ingen sida för dem.

Det skiljer sig från en annan sorts tomt resultat. När PDF till CSV inte hittar någon tabell i PDF-filen svarar servern med 204 och pdfx avslutas med 1 och rapporterar no_content: verktyget skulle ha producerat innehåll och gjorde det inte, och orsaken finns i Tom tabellexport (204): din PDF har troligen inga kolumner. För ett filterverktyg är "ingen träff" det svar det var tänkt att ge.

För att läsa det i ett skript använder du --json. En träff skriver ut filen som skrevs; ingen träff skriver ut { "matched": false }:

# Träff: filen skrivs och dess information skrivs ut
$ pdfx filter-page-count three.pdf --pageCount 2 --comparator Greater --json -o big/
{ "path": "big/three.pdf", "contentType": "application/pdf", "bytes": 2594 }

# Ingen träff: ingen fil skrivs
$ pdfx filter-page-count three.pdf --pageCount 5 --comparator Greater --json
{ "matched": false }

För att behålla bara filerna med fler än 2 sidor kan du skriva så här. Vi körde det lokalt på a.pdf (1 sida) och three.pdf (3 sidor), och bara den senare behölls:

mkdir -p big
for f in *.pdf; do
  if pdfx filter-page-count "$f" --pageCount 2 --comparator Greater --json -o big/ \
      | jq -e '.matched == false' >/dev/null; then
    echo "$f: hoppades över"
  fi
done

jq -e '.matched == false' avslutas med 0 när det inte finns någon träff och med 1 när det finns det. Vid träff har filen redan skrivits till big/ av pdfx; if avgör bara om "hoppades över" ska skrivas ut, inte om filen ska skrivas.

Med en enda indata saknar --json arrayen files

Med flera indatafiler är standard ut under --json en fullständig rapport. input är en absolut sökväg. processed räknar filerna vars begäran lyckades, inklusive de utan träff. unmatched är hur många av dem som saknade träff, och deras poster är { "ok": true, "matched": false } utan path. När alla filer i en batch saknar träff är slutkoden ändå 0. När du skickar --idempotency-key till en batch är nyckeln som skickas för varje fil <key>:<index>; mekanismen finns i Idempotency-Key: säkra återförsök för PDF-jobb.

{
  "processed": 2,
  "unmatched": 0,
  "failed": 1,
  "files": [
    { "input": "/work/a.pdf", "ok": true, "path": "out/a.pdf", "contentType": "application/pdf", "bytes": 1040 },
    { "input": "/work/broken.pdf", "ok": false, "error": "HTTP 400: The file is not a valid PDF or it is damaged.", "reason": "invalid_pdf" },
    { "input": "/work/b.pdf", "ok": true, "path": "out/b.pdf", "contentType": "application/pdf", "bytes": 1027 }
  ]
}

Med en enda indatafil används enfilsvägen: --json skriver ut { "path": ..., "contentType": ..., "bytes": ... } och det finns ingen array files. Vid fel är standard ut tomt och allt står på standard fel. När du expanderar filer med en glob måste skriptet hantera båda formaten, oavsett om en eller flera filer fick träff.

Samla reglerna ovan i ett enda CI-steg

Det här skriptet komprimerar bara och använder inget filterverktyg. Ett komprimeringsfel avslutas med 1, och steget misslyckas med det. Ett filterverktyg utan träff avslutas med 0, så att CI inte misslyckas bara för att det saknades träff; om avsaknad av träff är ett problem avgör du själv genom att läsa --json, som i filteravsnittet ovan.

Om någon fil avvisas misslyckas det här steget och listar filnamnen och orsakerna i loggen. Logiken är densamma som tidigare: läs slutkoden först, skriv ut standard fel vid fel och använd sedan jq för att plocka ut reason ur batchrapporten. Utan katalogargument avslutas det direkt, så att "$1"/*.pdf aldrig expanderas till /*.pdf.

#!/usr/bin/env bash
# Användning: ./ci-step.sh docs
docs="${1:?användning: ./ci-step.sh <katalog>}"
mkdir -p out
pdfx compress "$docs"/*.pdf -o out/ --json > report.json 2> errors.log
status=$?
if [ "$status" -ne 0 ]; then
  cat errors.log >&2
  jq -r '.files[]? | select(.ok | not) | "\(.input | split("/") | last)\t\(.reason)"' report.json >&2
fi
exit "$status"

Vi körde det lokalt med fyra sorters indata:

Filer i katalogen Slutkod Logg
Två fungerande PDF-filer 0 ingen
Två fungerande PDF-filer plus en skadad 1 pdfx: broken.pdf: HTTP 400: ..., sedan en rad broken.pdf invalid_pdf
En enda fungerande PDF 0 ingen; report.json har enfilsformatet
En enda skadad PDF 1 reason: invalid_pdf, code: invalid_document och en hint-rad; report.json är tom

Frågetecknet i slutet av .files[]? i jq hindrar enfilsrapporten (som saknar files) från att orsaka fel. En enda skadad fil har ingen batchrapport och orsaken syns bara i errors.log, så behåll båda utdataströmmarna.

Ytterligare tre saker att kontrollera innan du lägger detta i CI. pdfx behöver nätverk: filerna laddas upp till PDFX_API_BASE, som som standard är https://pdf123.xyz. För dokument som måste stanna i ditt nätverk kör du en egen tjänst och pekar den här variabeln mot den; se Vad du faktiskt vinner på att köra PDF123 själv (och vad det kostar). När du behöver en identitet lägger du nyckeln i PDFX_API_KEY och inte i kommandoradsargument, där den blir kvar i processlistan och i loggarna. Den version som är publicerad just nu är 0.1.0; i CI använder du npx @pdf123/[email protected] ... för att låsa den, så att utdataformatet och slutkoderna inte ändras med nya utgåvor och en uppgradering blir en ändring du gör med avsikt.

Paketets sida på npm är @pdf123/cli.

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool