====== 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]]).