====== std.env — Umgebung und Kommandozeile ====== → [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]] Zugriff auf **Umgebungsvariablen** und **Kommandozeilenargumente** — beides direkt aus dem Startstack, ohne C-Bibliothek. Der Kernel legt beim Prozessstart hinter ''argv'' den Zeigervektor der Umgebung ab; ''std.env'' liest ihn dort aus. Einsatzbereiche: CLI-Werkzeuge, Dienste und Skript-Wrapper, die ihre Konfiguration aus ''--flag=wert'' oder Umgebungsvariablen beziehen. **Autor:** Andreas Röne\\ **Copyright:** 2024–2025 Andreas Röne\\ **Quelle:** ''std/env.lyx'' ''std.env'' ist die **Grundlage** für die Umgebungsfunktionen in [[lyx_-_programmiersprache:units:os|std.os]]: ''env_get'' ruft intern ''EnvLookupRaw'' auf. Wer nur lesen will, kommt mit ''std.env'' aus; wer Werte zur Laufzeit **setzen** will, braucht ''std.os'' — siehe [[#Zusammenspiel mit std.os]]. ===== Import ===== import std.env; ---- ===== Funktionen ===== ==== Umgebungsvariablen ==== ^ Signatur ^ Beschreibung ^ | ''EnvLookupRaw(name: pchar): pchar'' | Wert der Variablen ''name'', oder ''0'' wenn nicht gesetzt. Der Zeiger zeigt **in die Umgebung**, es wird nichts kopiert | | ''EnvBase(): int64'' | Adresse des ''envp''-Vektors, oder 0 wenn ''argv'' nicht gesichert wurde | | ''EnvEntryValueOff(entry: pchar, key: pchar): int64'' | Prüft einen Eintrag der Form ''NAME=WERT'' gegen ''key''; liefert den Offset des Werts im Eintrag oder ''-1'' | ''EnvBase'' und ''EnvEntryValueOff'' sind die Bausteine von ''EnvLookupRaw'' und im Alltag selten nötig — sie sind öffentlich, damit man eigene Durchläufe über die gesamte Umgebung schreiben kann. > **Auch unter Windows nutzbar.** ''EnvBase'' unterstellte das SysV-Speicherbild (''envp'' hinter ''argv''); beim Ziel ''win64'' stürzte ''EnvLookupRaw'' deshalb sofort ab — samt allem, was darauf aufsetzt (''env_get'' aus ''std.os'', die Zeitzonenauswahl in ''std.datetime''). Seit lyxc 1.1.3F wird dort der Windows-Umgebungsblock gelesen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1677|#1677]]); nachgemessen mit einem PE unter Wine. ==== Kommandozeile ==== ^ Signatur ^ Beschreibung ^ | ''ArgvGetStr(argc: int64, argv: pchar, name: pchar): pchar'' | Sucht ein Argument der Form ''--name=wert'' und gibt den Wertteil zurück, sonst ''0'' | Dazu die Compiler-Builtins, die den Startstack bereitstellen: ^ Builtin ^ Beschreibung ^ | ''GetArgC(): int64'' | Anzahl der Argumente einschließlich Programmname | | ''GetArgV(): pchar'' | Basis des ''argv''-Zeigervektors | | ''ArgvGet(argv: pchar, i: int64)'' | Argument ''i''; außerhalb von ''0…argc'' kommt der Nullzeiger zurück | ---- ===== Beispiele ===== ==== Umgebungsvariablen lesen ==== import std.env; fn zeigVar(name: pchar): void { var v: pchar := EnvLookupRaw(name); if (v != 0 as pchar) { PrintLn(" ", name, " = ", v); } else { PrintLn(" ", name, " ist nicht gesetzt"); } } fn main(): int64 { zeigVar("HOME"); zeigVar("LANG"); zeigVar("GIBTESNICHT_XYZ"); // Praefixe schlagen nicht faelschlich an zeigVar("HOM"); zeigVar("HOMEX"); return 0; } Ausgabe: HOME = /home/andreas LANG = de_DE.UTF-8 GIBTESNICHT_XYZ ist nicht gesetzt HOM ist nicht gesetzt HOMEX ist nicht gesetzt Der Vergleich ist exakt: ''HOM'' schlägt bei einem Eintrag ''HOME=…'' nicht an, weil hinter dem Namen zwingend ein ''='' stehen muss. Werte dürfen Leerzeichen enthalten — der Eintrag endet erst am Nullbyte. **Nicht gesetzt** wird durch den Nullzeiger angezeigt, nicht durch einen leeren String. Eine gesetzte, aber leere Variable (''FOO='') liefert einen gültigen Zeiger auf einen leeren String — der Unterschied ist also erkennbar. ==== Kommandozeilenargumente auswerten ==== import std.env; fn hole(name: pchar, standard: pchar): pchar { var v: pchar := ArgvGetStr(GetArgC(), GetArgV(), name); if (v == 0 as pchar) { return standard; } return v; } fn main(): int64 { PrintLn("Aufruf mit ", IntToStr(GetArgC()), " Argumenten:"); var i: int64 := 0; while (i < GetArgC()) { PrintLn(" argv[", IntToStr(i), "] = ", ArgvGet(GetArgV(), i) as pchar); i := i + 1; } PrintLn("Konfiguration:"); PrintLn(" Ausgabedatei: ", hole("output", "a.out")); PrintLn(" Stufe: ", hole("level", "1")); PrintLn(" Modus: ", hole("mode", "standard")); return 0; } Aufruf ''./d2 --output=bericht.pdf --level=3'': Aufruf mit 3 Argumenten: argv[0] = ./d2 argv[1] = --output=bericht.pdf argv[2] = --level=3 Konfiguration: Ausgabedatei: bericht.pdf Stufe: 3 Modus: standard Das Muster ''hole(name, standard)'' ist die übliche Form: ''ArgvGetStr'' liefert ''0'' bei fehlendem Argument, der Aufrufer setzt den Vorgabewert ein. ---- ===== Zusammenspiel mit std.os ===== ''std.os'' bietet ''env_get'', ''env_set'', ''env_unset'' und ''env_has''. Der Lesepfad landet bei ''EnvLookupRaw''; Schreiben geht dagegen **nicht** in den Startstack, sondern in eine prozesseigene Überlagerung, die ''env_get'' vorgeschaltet ist. import std.env; import std.os; fn main(): int64 { PrintLn("env_get(HOME) = ", env_get("HOME")); PrintLn("EnvLookupRaw(HOME) = ", EnvLookupRaw("HOME")); PrintLn("env_has(MEINE_VAR) = ", IntToStr(env_has("MEINE_VAR") as int64)); env_set("MEINE_VAR", "gesetzt-zur-laufzeit"); PrintLn("nach env_set:"); PrintLn(" env_get(MEINE_VAR) = ", env_get("MEINE_VAR")); var raw: pchar := EnvLookupRaw("MEINE_VAR"); PrintLn(" EnvLookupRaw(MEINE_VAR) gesetzt? ", IntToStr((raw != 0 as pchar) as int64), " <- Ueberlagerung nicht sichtbar"); env_unset("HOME"); PrintLn("nach env_unset(HOME):"); PrintLn(" env_get(HOME) = '", env_get("HOME"), "'"); PrintLn(" EnvLookupRaw(HOME) = '", EnvLookupRaw("HOME"), "' <- Startstack unveraendert"); return 0; } Ausgabe: env_get(HOME) = /home/andreas EnvLookupRaw(HOME) = /home/andreas env_has(MEINE_VAR) = 0 nach env_set: env_get(MEINE_VAR) = gesetzt-zur-laufzeit EnvLookupRaw(MEINE_VAR) gesetzt? 0 <- Ueberlagerung nicht sichtbar nach env_unset(HOME): env_get(HOME) = '' EnvLookupRaw(HOME) = '/home/andreas' <- Startstack unveraendert Daraus folgen drei Regeln: * Wer mit ''env_set'' arbeitet, muss durchgängig ''env_get'' lesen — ''EnvLookupRaw'' sieht die Überlagerung nicht. * ''env_unset'' entfernt nichts aus dem Startstack, es blendet den Namen nur für ''env_get'' aus. * Ein per ''execve'' gestartetes Kind erbt die Überlagerung **nicht** — es bekommt die unveränderte Umgebung des Elternprozesses. Unterschiedlich ist auch der Rückgabewert bei fehlender Variable: ''EnvLookupRaw'' liefert den **Nullzeiger**, ''env_get'' einen **leeren String**. Bei ''env_get'' sind „nicht gesetzt" und „auf leer gesetzt" daher nicht unterscheidbar; dafür gibt es ''env_has''. ---- ===== Fallstricke ===== Geprüft mit ''lyxc 1.0.21A''. ==== Argumentschleifen enden am Nullzeiger ==== ''argv[argc]'' ist der abschließende Nullzeiger — die verlässliche Abbruchbedingung. Seit dem Fix von #1460 liefert auch jeder Index darüber hinaus 0, statt in die dahinterliegende Umgebung zu lesen: argc = 2 argv[argc] = 0 argv[argc+1] = 0 Ein Off-by-one führt damit zu einem Nullzeiger statt zu einer Zeichenkette aus der Umgebung. Schleifen trotzdem gegen ''GetArgC()'' begrenzen — das ist die klarere Absicht. ==== Der Rückgabewert zeigt in die Umgebung ==== ''EnvLookupRaw'' kopiert nicht. Der Zeiger bleibt gültig, solange der Prozess lebt — die Startstack-Umgebung wird nicht verändert. Wer den Wert aber selbst umschreiben will, muss ihn vorher kopieren; ein ''poke8'' auf den Rückgabewert verändert die Umgebung des laufenden Prozesses. ==== ArgvGet in PrintLn braucht einen Cast ==== Ohne ''as pchar'' gibt ''PrintLn(ArgvGet(GetArgV(), i))'' die Zeigeradresse als Zahl aus: argv[0] = 140721192761205 Mit ''ArgvGet(GetArgV(), i) as pchar'' erscheint der Text. Das betrifft nur die Ausgabe; ''ArgvGetStr'' und ''StrLen'' verarbeiten den Wert auch ohne Cast korrekt. ==== Der Flag-Name wird ohne führende Striche übergeben ==== ''ArgvGetStr(argc, argv, "output")'' findet ''--output=…''. Die beiden Striche prüft die Funktion selbst — ''ArgvGetStr(argc, argv, "--output")'' findet folglich nur ''----output=…'' und damit praktisch nichts. Frühere Fassungen dieser Seite zeigten die Form mit Strichen. ==== Nur --name=wert, nichts anderes ==== Nicht unterstützt: getrennte Form (''--output datei''), Kurzformen (''-o wert''), Schalter ohne Wert (''--verbose'') und die Endemarke ''--''. ''--name='' mit leerem Wert liefert einen gültigen Zeiger auf einen leeren String, nicht ''0''. Wer mehr braucht, wertet ''argv'' selbst aus. ==== Obergrenze von 8192 Einträgen ==== ''EnvLookupRaw'' bricht nach 8192 Umgebungseinträgen ab und meldet „nicht gesetzt". In der Praxis unkritisch — die üblichen Grenzen des Kernels für Umgebungsgröße greifen weit vorher. ---- ===== Verwandte Units ===== * [[lyx_-_programmiersprache:units:os|std.os]] — ''env_get''/''env_set''/''env_unset''/''env_has'', Arbeitsverzeichnis, Prozess-IDs * [[lyx_-_programmiersprache:units:string|std.string]] — ''StrSub'', ''StrLen'' für die Nachbearbeitung der Werte * [[lyx_-_programmiersprache:units:conv|std.conv]] — Argumentwerte in Zahlen wandeln ---- Letzte Aktualisierung: 2026-08-19 — Hinweis zum Windows-Umgebungsblock ergänzt ([[https://github.com/SEOLizer/LyX-Compiler/issues/1677|#1677]]).