— user@ocaml-programming —
user@ocaml-programming $ cat ./ocaml-programming.md (main)

ocaml-programming

# OCaml für die Praxis — eine Tutorial-Reihe.

~/ / docs / Praxis / 08 · Praxis

Ein kleines CLI-Tool bauen

Vom leeren Projekt zu einem brauchbaren Kommandozeilen-Tool — mit Argument-Parsing, Dateilesen und sauberer Fehlerbehandlung.

Zeit für ein Endprodukt. In diesem Kapitel entsteht wcount — ein Mini-wc-Klon, der Zeilen, Wörter und Zeichen in einer Datei zählt. Klein, aber realistisch genug, um die Bausteine der bisherigen Kapitel zusammenzubringen.

Gerüst

Neues dune-Projekt anlegen:

dune init project wcount --kind=executable
cd wcount

In bin/dune werden zwei Pakete als Abhängigkeiten ergänzt — cmdliner für Argument-Parsing und stdio für I/O-Hilfsfunktionen:

(executable
 (name main)
 (public_name wcount)
 (libraries cmdliner stdio))

Einmal installieren:

opam install cmdliner stdio

Kernlogik

In bin/main.ml wird zuerst die Zählfunktion definiert. Sie nimmt den Dateiinhalt als String und liefert ein Record mit den drei Zahlen:

type stats = { lines : int; words : int; chars : int }

let count_text text =
  let chars = String.length text in
  let lines =
    String.fold_left
      (fun acc c -> if c = '\n' then acc + 1 else acc) 0 text
  in
  let words =
    text
    |> String.split_on_char ' '
    |> List.filter (fun s -> String.length s > 0)
    |> List.length
  in
  { lines; words; chars }

|> ist der Pipe-Operator — er reicht den Wert links als letztes Argument an die Funktion rechts weiter. Das macht die Wort-Pipeline gut lesbar von oben nach unten.

Fehlerbehandlung

Lesen kann scheitern. Das Ergebnis kapselt ein result-Wert:

let read_safe path =
  try Ok (Stdio.In_channel.read_all path)
  with Sys_error msg -> Error msg

let run path =
  match read_safe path with
  | Ok text ->
      let s = count_text text in
      Printf.printf "%6d %6d %6d %s\n" s.lines s.words s.chars path;
      0
  | Error msg ->
      Printf.eprintf "wcount: %s\n" msg;
      1

Der Rückgabewert von run ist der Exit-Code — Konvention: 0 für Erfolg.

Argumente parsen

cmdliner macht aus Funktionen deklarative CLI-Definitionen. Für ein einziges Positionsargument reicht das hier:

open Cmdliner

let path_arg =
  let doc = "Pfad zur Datei." in
  Arg.(required & pos 0 (some file) None & info [] ~docv:"FILE" ~doc)

let cmd =
  let info = Cmd.info "wcount" ~doc:"Zeilen, Wörter und Zeichen zählen." in
  Cmd.v info Term.(const run $ path_arg)

let () = exit (Cmd.eval' cmd)

Bauen und ausprobieren:

dune build
dune exec wcount -- README.md

    42    180   1240 README.md

--help ist von cmdliner automatisch dabei:

dune exec wcount -- --help

Das war’s. In gut sechzig Zeilen Code steht ein vorzeigbares Werkzeug — let-Bindungen, Records, Pattern Matching, result-Fehler, dune-Setup und externe Bibliotheken arbeiten zusammen. Damit endet die Einstiegsreihe. Ab hier lohnt der Sprung in echte Projekte: eine Bibliothek veröffentlichen, einen kleinen Webserver mit Dream oder Opium bauen, oder die Standardbibliothek vertiefen.