Zum Inhalt springen
aviral gupta

// B1.3 · ca. 33 Min. · Einstieg

Argumente mit process.argv und util.parseArgs

Nach dieser Lektion lesen Sie, was nach node main.js eingegeben wurde, beschreiben die Optionen Ihres Befehls mit util.parseArgs und machen aus einem falschen Argument eine klare Meldung statt eines Absturzes.

Lektion 3 von 5 in B1 Node ausführen und Projektgrundlagen

Danach können Sie

  • Die Argumente aus process.argv.slice(2) lesen und aus Strings umwandeln
  • Optionen mit util.parseArgs beschreiben (type, short, default, multiple) und values und positionals lesen
  • Die Fehler vorhersagen, die parseArgs im Strict-Modus wirft, und sie mit einer hilfreichen Meldung abfangen
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen: Sie führen node main.js Ada 3 aus. Welcher Ausdruck in main.js ist der String "Ada"?

    node main.js Ada 3
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen. main.js enthält diese zwei Zeilen, und Sie führen node main.js 2 3 aus. Was gibt es aus?

    const [a, b] = process.argv.slice(2);
    console.log(a + b);
  3. Üben · Aufgabe 3 von 7

    Setzen Sie die Methode ein, damit node main.js one two nur die eingegebenen Argumente ausgibt: [ 'one', 'two' ].

    const args = process.argv.____(2);
    console.log(args);
    const args = process.argv.(2);
  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jeder Einstellung einer parseArgs-Option zu, was sie bewirkt.

  5. Üben · Aufgabe 5 von 7

    Die Option name wird zweimal verwendet. Was gibt dieses Programm aus?

    import {parseArgs} from 'node:util';
    
    const {values} = parseArgs({
      args: ['--name', 'Ada', '--name', 'Grace'],
      options: {name: {type: 'string'}}
    });
    console.log(values.name);
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Sie führen node main.js -v report.txt aus. Was passiert?

    import {parseArgs} from 'node:util';
    
    const {values, positionals} = parseArgs({
      options: {verbose: {type: 'boolean', short: 'v'}}
    });
    console.log(values.verbose, positionals);
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. Schreiben Sie main.js für einen Grußbefehl: node main.js Ada Grace gibt Hello, Ada! und Hello, Grace! aus. Beschreiben Sie drei Optionen mit parseArgs: --shout (-s) gibt in Großbuchstaben aus, --greeting (-g) ersetzt Hello, und --times (-t) wiederholt jede Zeile, umgewandelt mit Number(). Erlauben Sie Positionals. Wirft parseArgs, geben Sie seine Meldung und eine Usage-Zeile aus statt eines Stacktrace. Probieren Sie es ohne Namen, mit -st 2 Ada und mit --colour Ada.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Parser, drei Kommandozeilen

Dieses Programm zeigt zuerst, was process.argv enthält, wenn Sie einfach node main.js ausführen: zwei Einträge, das Programm node und das Skript, und nichts dahinter. Dann beschreibt es zwei Optionen, einen Schalter --loud (-l) und --times (-t) mit Standardwert, und gibt parseArgs drei Kommandozeilen als Arrays aus Strings, so wie process.argv.slice(2) sie liefern würde. Achten Sie darauf, wie jeder Wert ankommt: als true, als String oder als Standardwert.

main.js

import {basename} from 'node:path';
import {parseArgs} from 'node:util';

// Run as plain node main.js: no arguments after the script.
console.log(process.argv.length, basename(process.argv[1]), process.argv.slice(2));

const options = {
  loud: {type: 'boolean', short: 'l'},
  times: {type: 'string', short: 't', default: '1'}
};

// The same parser, given three command lines as arrays of strings.
for (const args of [['Ada'], ['-l', '--times', '3', 'Ada'], ['--times=2', 'Ada', 'Grace']]) {
  const {values, positionals} = parseArgs({args, options, allowPositionals: true});
  console.log(args.join(' '), '->', values, positionals);
}

Ausführen mit

node main.js

Ausgabe

2 main.js []
Ada -> [Object: null prototype] { times: '1' } [ 'Ada' ]
-l --times 3 Ada -> [Object: null prototype] { loud: true, times: '3' } [ 'Ada' ]
--times=2 Ada Grace -> [Object: null prototype] { times: '2' } [ 'Ada', 'Grace' ]
  • process.argv hat auch ohne Argumente zwei Einträge; slice(2) ist leer.
  • times ist immer ein String, '3' und nicht 3, und '1', wenn die Option fehlt: Wandeln Sie ihn mit Number() um, bevor Sie damit zählen.
  • loud erscheint nur, wenn -l angegeben ist. Eine fehlende boolesche Option hat keinen Eintrag, values.loud ist also undefined, nicht false.
  • [Object: null prototype] ist die Art, wie Node.js das Objekt values ausgibt, das keinen Prototyp hat. Seine Eigenschaften funktionieren wie gewohnt.

Übungen

Übung 1 von 2

Eine kleine Kommandozeile von Hand zerlegen

Ein Befehl nimmt einen Text und eine optionale Anzahl: node main.js hello 3. Schreiben Sie readArgs(args); args ist das, was process.argv.slice(2) liefert. Geben Sie {text, times} zurück, times als Zahl, 1, wenn sie fehlt. Werfen Sie einen Error, dessen Meldung mit Usage beginnt, wenn kein Text angegeben ist, und einen Error, wenn times keine ganze Zahl ab 1 ist. Die Funktion bekommt ein Array, statt process.argv selbst zu lesen, damit die Tests jede Kommandozeile ausprobieren können, und sie läuft auch im Browser.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Destrukturierung kann einen Standardwert setzen: const [text, count = '1'] = args;

  2. Hinweis 2

    Number(count) macht aus "3" die Zahl 3; Number.isInteger sagt Ihnen, ob das Ergebnis eine ganze Zahl ist.

  3. Hinweis 3

    Ein Argument, das niemand eingegeben hat, ist undefined. Prüfen Sie text === undefined, bevor Sie es verwenden.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

// args is what process.argv.slice(2) gives: an array of strings.
export function readArgs(args) {
  const [text, count = '1'] = args;
  if (text === undefined) throw new Error('Usage: node main.js <text> [times]');
  const times = Number(count);
  if (!Number.isInteger(times) || times < 1) throw new Error('times must be a whole number from 1, got "' + count + '"');
  return {text, times};
}

console.log(readArgs(['hello', '3']));
Auf dem eigenen Computer ausführen

Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

// args is what process.argv.slice(2) gives: an array of strings.
export function readArgs(args) {
  const text = args[0];
  const times = args[1];
  return {text, times};
}

console.log(readArgs(['hello', '3']));

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {readArgs} from './main.js';

test('readArgs(["hello", "3"]) ist {text: "hello", times: 3}', () => {
  const got = readArgs(['hello', '3']);
  assert.deepEqual(got, {text: 'hello', times: 3}, `readArgs(["hello", "3"]) lieferte ${JSON.stringify(got)}`);
});

test('times ist 1, wenn nur der Text angegeben ist', () => {
  const got = readArgs(['hi']);
  assert.deepEqual(got, {text: 'hi', times: 1}, `readArgs(["hi"]) lieferte ${JSON.stringify(got)}`);
});

test('ohne Argumente: wirft einen Error, der mit Usage beginnt', () => {
  assert.throws(() => readArgs([]), {message: /^Usage/}, 'readArgs([]) sollte einen Error werfen, dessen Meldung mit Usage beginnt');
});

test('times, das keine ganze Zahl ab 1 ist, wirft', () => {
  assert.throws(() => readArgs(['hi', 'two']), Error, 'readArgs(["hi", "two"]) sollte werfen');
  assert.throws(() => readArgs(['hi', '0']), Error, 'readArgs(["hi", "0"]) sollte werfen');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 2 von 2

Ein echo-Befehl mit parseArgs

Vervollständigen Sie echo(args); die Funktion liefert den Text, den der Befehl ausgibt. Beschreiben Sie zwei Optionen für parseArgs: --upper (-u), einen Schalter, der die Wörter in Großbuchstaben setzt, und --times (-t), einen String mit dem Standardwert '1', der die Zeile wiederholt, je eine pro Zeile. Erlauben Sie Positionals: Das sind die Wörter, mit Leerzeichen verbunden. Wirft parseArgs, lassen Sie es nicht abstürzen: Geben Sie seine Meldung, einen Zeilenumbruch und USAGE zurück. Probieren Sie node main.js -u -t 2 hello world, dann node --test.

Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.

Hinweise
  1. Hinweis 1

    Beschreiben Sie jede Option unter ihrem langen Namen: upper: {type: 'boolean', short: 'u'}.

  2. Hinweis 2

    times ist eine String-Option mit default: '1'. Array(Number(values.times)).fill(line).join('\n') wiederholt die Zeile.

  3. Hinweis 3

    Setzen Sie den Aufruf von parseArgs in try/catch und geben Sie im catch error.message + '\n' + USAGE zurück.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import {parseArgs} from 'node:util';

export const USAGE = 'Usage: node main.js [--upper] [--times N] <words...>';

// Returns what the command prints for these arguments.
export function echo(args) {
  try {
    const {values, positionals} = parseArgs({
      args,
      options: {
        upper: {type: 'boolean', short: 'u'},
        times: {type: 'string', short: 't', default: '1'}
      },
      allowPositionals: true
    });
    const text = positionals.join(' ');
    const line = values.upper ? text.toUpperCase() : text;
    return Array(Number(values.times)).fill(line).join('\n');
  } catch (error) {
    return error.message + '\n' + USAGE;
  }
}

console.log(echo(process.argv.slice(2)));
Auf dem eigenen Computer ausführen

Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

import {parseArgs} from 'node:util';

export const USAGE = 'Usage: node main.js [--upper] [--times N] <words...>';

// Returns what the command prints for these arguments.
export function echo(args) {
  const {values, positionals} = parseArgs({args, options: {}, allowPositionals: true});
  return positionals.join(' ');
}

console.log(echo(process.argv.slice(2)));

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';
import {echo, USAGE} from './main.js';

const attempt = (args) => {
  try {
    return echo(args);
  } catch (error) {
    return `warf ${error.message}`;
  }
};

test('echo(["hello", "world"]) ist "hello world"', () => {
  const got = attempt(['hello', 'world']);
  assert.equal(got, 'hello world', `echo(["hello", "world"]) lieferte ${JSON.stringify(got)}`);
});

test('--upper und -u setzen die Wörter in Großbuchstaben', () => {
  assert.equal(attempt(['--upper', 'hi']), 'HI', `echo(["--upper", "hi"]) lieferte ${JSON.stringify(attempt(['--upper', 'hi']))}`);
  assert.equal(attempt(['-u', 'hi']), 'HI', `echo(["-u", "hi"]) lieferte ${JSON.stringify(attempt(['-u', 'hi']))}`);
});

test('--times 3 wiederholt die Zeile dreimal, je eine pro Zeile', () => {
  const got = attempt(['--times', '3', 'hi']);
  assert.equal(got, 'hi\nhi\nhi', `echo(["--times", "3", "hi"]) lieferte ${JSON.stringify(got)}`);
});

test('eine unbekannte Option liefert ihre Meldung und die Usage-Zeile', () => {
  const got = attempt(['--colour', 'hi']);
  assert.ok(got.startsWith("Unknown option '--colour'") && got.endsWith('\n' + USAGE), `echo(["--colour", "hi"]) lieferte ${JSON.stringify(got)}`);
});

test('node main.js -u -t 2 hello world gibt zweimal HELLO WORLD aus', async () => {
  const got = (await runMain({args: ['-u', '-t', '2', 'hello', 'world']})).trimEnd();
  assert.equal(got, 'HELLO WORLD\nHELLO WORLD', `das Programm gab ${JSON.stringify(got)} aus`);
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Häufige Fehler

Ein Argument verwenden, das nicht angegeben wurde

const name = process.argv[2];
console.log("Hello, " + name.toUpperCase() + "!");

Was Node.js ausgibt

TypeError: Cannot read properties of undefined (reading 'toUpperCase')

Warum, und die Lösung

Mit node main.js ohne etwas dahinter ist process.argv[2] undefined, und eine Methode auf undefined aufzurufen wirft. Prüfen Sie ein Argument, bevor Sie es verwenden: Bei name === undefined geben Sie eine Usage-Zeile wie Usage: node main.js <name> aus und hören auf, oder Sie setzen einen Standardwert mit process.argv[2] ?? "world".

parseArgs nach type: 'number' fragen

import {parseArgs} from 'node:util';

const {values} = parseArgs({options: {port: {type: 'number'}}});
console.log(values.port);

Was Node.js ausgibt

TypeError [ERR_INVALID_ARG_TYPE]: The "options.port.type" property must be ('string|boolean'). Received type string ('number')

Warum, und die Lösung

parseArgs kennt nur zwei Typen: 'boolean' für einen Schalter und 'string' für eine Option mit Wert. Beschreiben Sie den Port als type: 'string' und wandeln Sie selbst um: const port = Number(values.port), geprüft mit Number.isInteger, bevor Sie ihn verwenden. Der Fehler kommt, bevor ein Argument gelesen wird, weil schon die Beschreibung falsch ist.

allowPositionals vergessen

import {parseArgs} from 'node:util';

// What node main.js --lines 5 notes.txt passes to the program:
const args = ['--lines', '5', 'notes.txt'];
const {values, positionals} = parseArgs({args, options: {lines: {type: 'string'}}});
console.log(values.lines, positionals);

Was Node.js ausgibt

TypeError [ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL]: Unexpected argument 'notes.txt'. This command does not take positional arguments

Warum, und die Lösung

Im Strict-Modus, der Voreinstellung, ist allowPositionals false: Jedes Argument muss eine Option sein, die Sie beschrieben haben. Ein Dateiname wie notes.txt ist ein Positional, also ergänzen Sie allowPositionals: true. Dann ist values.lines gleich '5' und positionals gleich [ 'notes.txt' ].

JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

process.argv: die Kommandozeile als Strings

process.argv ist ein Array aus Strings. Index 0 ist der Pfad des Programms node, Index 1 der absolute Pfad Ihres Skripts, und was nach dem Skript eingegeben wurde, beginnt bei Index 2: process.argv.slice(2) ist das, was Ihr Programm bekommen hat. Flags für Node.js selbst, die vor dem Skript stehen wie in node --env-file=.env main.js, sind nicht darin; sie stehen in process.execArgv. Die Shell trennt die Zeile an Leerzeichen, Anführungszeichen halten "Ada Lovelace" als ein Argument zusammen. Jedes Argument ist ein String, also ergibt "2" + "3" den Wert "23": Wandeln Sie mit Number() um. Ein Argument, das niemand eingegeben hat, ist undefined.

util.parseArgs beschreibt Ihre Optionen

Importieren Sie {parseArgs} aus 'node:util' und beschreiben Sie jede Option unter ihrem langen Namen: type ist 'boolean' oder 'string' (einen Zahlentyp gibt es nicht), short ist ein Kürzel aus einem Buchstaben, default gilt, wenn die Option fehlt, und multiple: true sammelt jede Verwendung in einem Array; sonst gewinnt die letzte. Es versteht --name Ada, --name=Ada, -n Ada und gruppierte Flags wie -ln. Zurück kommen values, ausgegeben als [Object: null prototype] { … }, und positionals, die einfachen Argumente. args ist ohne Angabe process.argv ohne die ersten beiden Einträge; übergeben Sie ein eigenes Array, um den Parser auszuprobieren. Die Doku führt util als Stability: 2 - Stable, und parseArgs ist seit v20.0.0 nicht mehr experimentell.

Standardmäßig strikt: Ein falsches Argument wirft

strict ist true, solange Sie es nicht abschalten, und dann ist allowPositionals false. parseArgs wirft einen TypeError mit einem Code: ERR_PARSE_ARGS_UNKNOWN_OPTION für eine Option, die Sie nicht beschrieben haben (Unknown option '--colour'), ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL für ein einfaches Argument wie notes.txt, wenn Positionals nicht erlaubt sind, und ERR_PARSE_ARGS_INVALID_OPTION_VALUE, wenn eine String-Option keinen Wert hat oder eine boolesche einen bekommt. Nimmt Ihr Befehl Dateinamen, setzen Sie allowPositionals: true. Fangen Sie den Fehler ab und geben Sie seine Meldung mit einer Usage-Zeile aus. strict: false akzeptiert stattdessen jede Option, sodass ein Tippfehler unbemerkt bleibt. Alles nach -- ist ein Positional.

Quellen

Zuletzt geprüft am 30. September 2026