mitmario.dev

Eingaben lesen

Node.js Sandbox 4 Min Lesezeit 2 BeispieleLektion 4 von 6

Argumente kommen beim Start, Umgebungsvariablen kommen von außen. Es bleibt ein dritter Weg: Text, der während der Laufzeit hereinkommt. Die Standardeingabe, kurz stdin.

Sie ist das Gegenstück zu den beiden Kanälen aus Lektion 4.3. Dort ging etwas hinaus, hier kommt etwas herein, und es sind dieselben Rohre.

Es gibt zwei Arten, sie zu benutzen, und sie führen zu ganz verschiedenen Werkzeugen.

Der Weg mit der Frage

Wenn ein Mensch davorsitzt, willst du fragen können. Dafür gibt es node:readline, und in der modernen Fassung mit Promises liest es sich wie normaler Code.

Eine Frage stellen mit readline
import { createInterface } from "node:readline/promises";

const rl = createInterface({
  input: process.stdin,
  output: process.stdout,
});

const name = await rl.question("Wie heisst du? ");
console.log(`Hallo, ${name}!`);

rl.close();

rl.question gibt ein Promise zurück, await wartet darauf. Erst wenn jemand etwas getippt und Enter gedrückt hat, geht es weiter.

Auf dem Bildschirm stehen dann beide in einer Zeile: die Frage, die dein Programm geschrieben hat, und dahinter das, was der Mensch getippt hat. So sieht ein Terminal aus, es zeigt Eingetipptes einfach mit an.

Das rl.close() am Ende ist wichtiger, als es aussieht. Solange die Schnittstelle offen ist, wartet Node weiter auf Eingaben, und dein Programm hört nicht auf. Das ist derselbe Mechanismus wie in Lektion 1.2: Node beendet sich, wenn nichts mehr wartet. Ein offenes readline ist etwas, das wartet.

Starte einmal die Sandbox und schau in das Terminal. Dieses Beispiel läuft dann, und was dort steht, ist der ganze Punkt dieser Lektion. Die Frage steht da, eine Antwort kommt nicht, und auf dem Fehlerkanal liegt eine Zeile, die man sonst selten zu sehen bekommt:

Warning: Detected unsettled top-level await at file:///vercel/sandbox/lern/fragen.mjs:8

Node sagt damit: Dieses await ist nie fertig geworden, und es wartet auch nichts mehr darauf, also höre ich auf. Hier sitzt kein Mensch. Die Eingabe ist von vornherein zu Ende, rl.question bekommt nie eine Antwort, und weil rl.close() hinter dem await steht, wird auch das nie erreicht. Kein Absturz, keine Fehlermeldung, nur ein Programm, das auf etwas gewartet hat, das es hier nicht gibt.

Der Weg mit der Kette

Der zweite Fall kommt in der Praxis häufiger vor, und er sieht ganz anders aus. Jemand schiebt deinem Programm etwas hinein:

cat einkauf.txt | node zaehlen.mjs

Hier sitzt kein Mensch davor. Es gibt nichts zu fragen, es liegt einfach Text an, und dein Programm soll ihn verarbeiten.

Hereingeleiteten Text am Stück lesen
let text = "";

for await (const stueck of process.stdin) {
  text += stueck;
}

const zeilen = text.trim().split("\n");

console.log("Zeilen:", zeilen.length);
console.log("Erste:", zeilen[0]);
console.log("Letzte:", zeilen.at(-1));

process.stdin lässt sich mit for await durchgehen. Die Schleife bekommt den Text in Stücken, so wie er ankommt, und endet, wenn nichts mehr kommt. Danach liegt alles in einer Variablen.

Für kleine Eingaben ist das genau richtig. Für eine sehr große Datei nicht, denn dann liegt sie komplett im Arbeitsspeicher. Wie man stattdessen stückweise arbeitet, ohne alles zu sammeln, ist das Thema von Abschnitt 7.

Läuft dieses Beispiel ohne Eingabe, steht im Terminal Zeilen: 1, und hinter Erste: und Letzte: jeweils nichts. Eine Zeile, obwohl gar nichts hereinkam. Das ist keine Merkwürdigkeit der Sandbox, sondern von split: Ein leerer Text zerfällt in genau ein Stück, nämlich das leere.

Und jetzt leite selbst etwas hinein. Das Terminal ist eine Shell, eine Pipe gehört zu dem Wenigen, was eine Shell wirklich gut kann, und dieselbe Maschine, die das Beispiel gerade gefahren hat, steht dir dort zur Verfügung. Hol dieses Beispiel nach vorn, starte die Sandbox und tipp:

printf 'Brot\nMilch\nKaese\n' | node zaehlen.mjs

Dann steht im Terminal Zeilen: 3, darunter Erste: Brot und Letzte: Kaese.

Das ist derselbe Code, dieselbe Datei, derselbe Rechner. Geändert hat sich nur, dass jetzt jemand etwas hineinschiebt.

Warum der zweite Weg meistens der bessere ist

Ein Werkzeug, das Fragen stellt, kann nur von einem Menschen benutzt werden. Sobald jemand es in ein Skript einbaut, hängt das Skript und niemand weiß warum.

Ein Werkzeug, das liest, was hereinkommt, und schreibt, was herauskommt, lässt sich beliebig verketten. Genau darauf beruht die gesamte Unix-Werkzeugkiste: grep, sort, wc und die anderen stellen keine Fragen, sie nehmen etwas entgegen und geben etwas zurück.

Die brauchbare Mischung: Nimm die Werte über Argumente und die Daten über stdin, und frag nur dann, wenn wirklich etwas fehlt. So funktioniert dein Werkzeug in beiden Welten.

Warum es zu dieser Lektion keine Aufgabe gibt

Diese Lektion hat keine Challenge, und der Grund gehört zum Stoff.

Der Prüfknopf startet deinen Code in einer Sandbox, und der Weg, auf dem er dorthin fährt, hat keine Eingabe: Dein Programm bekommt sie so, wie du es oben gesehen hast, nämlich sofort zu Ende. Eine Prüfliste könnte hier also nur abfragen, was dein Werkzeug ohne jede Eingabe tut, und das ist ausgerechnet der Fall, um den es in dieser Lektion nicht geht.

Im Terminal gibt es die Eingabe sehr wohl, sie kommt nur nicht von einer Tastatur, sondern aus einer Pipe. Deshalb steht der Handgriff weiter oben statt in einer Aufgabe: Er funktioniert, er wird nur nicht bewertet.

Was du mitnehmen sollst, ist ohnehin eine Entwurfsentscheidung und kein Handgriff: Frag dich bei jedem Werkzeug, ob es fragen muss oder ob es auch lesen könnte.

Zum Mitnehmen

Ein Werkzeug, das sich in eine Kette einbauen lässt, ist mehr wert als eins, das Fragen stellt.

Jetzt du

Basis Konto, kostenlos

Im Editor änderst du die Beispiele dieser Lektion und lässt sie gleich laufen. So merkst du am schnellsten, ob es sitzt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 2 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.