Den exakten Änderungssatz erstellen

ShellBeginner
Jetzt üben

Einführung

Field Notes hat nach einem Anstieg der Teilnahme zwei Raumänderungen für Workshops genehmigt. Mehrere Sitzungstitel und Raumnamen wiederholen sich, weshalb ein sicherer Plan exakte Identitäten, einen genehmigten Umfang und ausreichend Zielkapazität benötigt.

Du siehst dir die Vorschau einer kleinen funktionierenden Änderung an und erstellst anschließend einen wiederverwendbaren PowerShell-Planvalidator. Dieses Lab setzt Objekte, JSON, Filter, Parameter und Fehlerbehandlung aus PowerShell for Beginners voraus; Bash-Skripte sind nicht erforderlich. Werkzeuge und ursprüngliche synthetische Ereignisdaten sind in einem separaten unabhängigen Arbeitsbereich vorbereitet. Die Dauer ist eine Autorenschätzung einschließlich Lesen und gewöhnlicher Fehler.

Einen vollständigen ID-basierten Plan erstellen

In diesem Schritt siehst du dir die Vorschau einer vorbereiteten Änderung an und erstellst anschließend einen wiederverwendbaren Validator, der den vollständigen fachlichen Plan speichert, ohne den Zeitplan zu ändern.

PowerShell starten und eine Anfrage in der Vorschau ansehen

Öffne Terminal. Zunächst läuft dort eine Linux-Shell. Der folgende Aufruf setzt UTF-8 nur für diese PowerShell-Sitzung; deine globale Shell bleibt unverändert. env übergibt dem Programm diese Umgebungswerte. -NoLogo blendet das Startbanner aus und -NoProfile überspringt persönliche Startskripte:

env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pwsh -NoLogo -NoProfile

Warte auf die mit PS beginnende Eingabeaufforderung. Alle weiteren Befehle laufen in PowerShell. Set-Location ändert das Arbeitsverzeichnis:

Set-Location /home/labex/project/room-changes

Das vorbereitete preview-one.ps1 verarbeitet nur die einzelne bereitgestellte Übungsanfrage. Es unterstützt PowerShells natives -WhatIf: eine Aktion beschreiben, statt sie auszuführen. Dies ist ein kleines funktionierendes Beispiel, noch nicht dein wiederverwendbarer Validator für mehrere Anfragen. ./ bezeichnet ein Skript im aktuellen Verzeichnis:

./preview-one.ps1 -WhatIf

Erwarte eine native Meldung mit Move s-a: r-old -> r-equal und dem Ziel ./rehearsal-only.json. Die IDs kennzeichnen die Sitzung sowie ihren bisherigen und vorgeschlagenen Raum. Es wurden nicht alle Sitzungen mit diesem Titel verschoben. Lies das tatsächliche Quell-JSON mit Get-Content -Raw, das die gesamte Datei als eine Zeichenfolge zurückgibt:

Get-Content ./event.json -Raw

Beim Datensatz s-a ist RoomId weiterhin gleich r-old. Test-Path prüft das Vorhandensein, ohne etwas anzulegen:

Test-Path ./rehearsal-only.json
False

Falls du -WhatIf versehentlich weggelassen hast, kann die Übung diese separate Datei anlegen, überschreibt aber die Eingabe nicht. Remove-Item -LiteralPath ./rehearsal-only.json entfernt nur diese Übungsausgabe; verwende den Befehl nur, wenn die Datei vorhanden ist, und wiederhole danach die Vorschau korrekt. -LiteralPath behandelt einen Pfad wörtlich, statt Platzhalterzeichen aufzulösen. Lösche zur Fehlerbehebung nicht das ursprüngliche Ereignis oder die Anfragen.

Vor dem Werkzeugbau die Beziehungen lesen

Der Veranstaltungsort hat bereits geprüft, dass die Zielräume zu diesen Zeiten frei sind. Du prüfst den genehmigten Umfang und die Kapazität, löst aber keine Kalenderkonflikte. Lies die angeforderten Zuordnungen und verwende den vorbereiteten schreibgeschützten Leser, um den tatsächlichen ursprünglichen Zeitplan anzusehen:

Get-Content ./requests.json -Raw
./Show-RoomSchedule.ps1 -Path ./event.json

Die Anfragen verschieben s-a nach r-equal und s-c nach r-large. s-a und s-b tragen beide den Titel Shared maps; nur s-a ist für diese Anfrage genehmigt. Auch zwei Räume heißen Studio. Verknüpfe über SessionId und RoomId, niemals über die angezeigten Namen. Die erste angeforderte Sitzung hat 24 Teilnehmende und ihr Zielraum genau 24 Plätze: Gleichheit ist erlaubt.

Die Originale event.json, requests.json, small-request.json, bad-request.json und die unabhängige notes.txt müssen erhalten bleiben, einschließlich Bytes, Dateimodus, Eigentümer und Änderungszeit (mtime). Bearbeite die vorbereiteten Werkzeuge nicht. Du darfst eigene Skripte und die in diesem Lab genannten neuen Ergebnisdateien erstellen. Die Kurspfade und ihre gesamte Verzeichniskette sind echte Verzeichnisse. Eine Ausgabe muss neu sein, sich von den Eingaben unterscheiden und ein bereits vorhandenes echtes Elternverzeichnis haben; vorhandene Dateien, Verzeichnisse oder Links, auch ins Leere zeigende Links, müssen unverändert abgelehnt werden. Arbeite ohne gleichzeitige andere Schreibzugriffe.

Das bereitgestellte JSON ist wohlgeformtes UTF-8: ein Ereignisobjekt mit Rooms, Sessions und ApprovedSessionIds sowie ein nicht leeres Anfragearray mit eindeutigen exakten Sitzungs-IDs und den Zielraum-IDs. IDs sind Zeichenfolgen mit Beachtung der Groß-/Kleinschreibung. Anzahlen und Kapazitäten sind nicht negative ganzzahlige Werte. Unbekannte Metadaten können verschachtelte Objekte, geordnete Arrays, Zeichenfolgen, boolesche Werte, null und begrenzte endliche JSON-Zahlen enthalten, bis zu 20 Verschachtelungsebenen. Exakt doppelte Objektschlüssel, fehlerhafte Schemas und beliebige bösartige Eingaben liegen außerhalb dieses Kurses.

Vor dem Speichern eines Plans jede Anfrage prüfen

Du erstellst Build-RoomPlan.ps1 mit drei benannten Parametern: EventPath, PlanPath und OutputPath. Es muss mit anderen bereitgestellten Ereignissen und IDs funktionieren und dabei diese Argumente und installierte Befehle ohne benachbarte Hilfsdateien verwenden. Das Ergebnis ist ein JSON-Array in Anfragereihenfolge. Jede Zeile enthält genau SessionId, OldRoomId, NewRoomId, Attendees und Capacity. Dies ist ein fachlicher Plan der aktuellen Objektbeziehungen, kein Marker, der eine Fertigstellung behauptet. Er ändert den Zeitplan nicht. Erst das nächste Lab schreibt ein vollständiges aktualisiertes Ereignis.

Lies diese Ergänzungen, bevor du das Skript speicherst:

  • [CmdletBinding()] macht daraus ein parametrisiertes erweitertes Skript; [Parameter(Mandatory)] verlangt ein Argument, und [string] deklariert Text. try, catch, throw und exit 1 folgen dem vorausgesetzten Lab zur Fehlerbehandlung. $ErrorActionPreference = 'Stop' lässt Befehlsfehler die aktuelle Operation beenden. Der catch-Block zeigt mit Write-Error -ErrorAction Continue die Erklärung an und erreicht anschließend das ausdrückliche exit 1.
  • PowerShells $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(...) löst die Ausgabe relativ zum aktuellen PowerShell-Verzeichnis auf. Split-Path -LiteralPath liefert den Elternpfad. Get-Item -Force findet auch verborgene Objekte und ins Leere zeigende Links. Hier erlaubt -ErrorAction SilentlyContinue das erwartete Fehlen einer neuen Ausgabe. Test-Path -PathType Container verlangt das Elternverzeichnis.
  • ConvertFrom-Json -AsHashtable behält JSON-Objekte als Wörterbücher bei, auch unbekannte Schlüssel, die sich nur in der Groß-/Kleinschreibung unterscheiden. -DateKind String erhält datumsähnliche Texte als Zeichenfolgen und bewahrt ihre ursprünglichen Zeitversätze; die vorbereitete PowerShell-Version unterstützt diese Option. -NoEnumerate hält ein Anfragearray auch mit nur einem Element zusammen.
  • @(...) sammelt Treffer als Array, auch bei null oder einem Treffer. -ceq vergleicht unter Beachtung der Groß-/Kleinschreibung. -ccontains prüft die exakte Mitgliedschaft; -cnotcontains ist die Umkehrung. -ne bedeutet ungleich, -gt größer als und -or, dass eine der Bedingungen genügt. -isnot [array] lehnt einen Wert ab, der kein Array ist.
  • $seen += ... und $changes += ... hängen Elemente an Sammlungen an. Eine unbekannte ID, eine nicht genehmigte Sitzung, eine doppelte Anfrage oder zu viele Teilnehmende lösen vor jeder Ausgabe einen Fehler aus. -gt erlaubt eine Kapazität genau gleich der Teilnahmezahl.
  • [pscustomobject]@{...} sammelt die bekannten Planfelder. ConvertTo-Json -InputObject $changes -Depth 5 serialisiert das vollständige Array; ausreichende Tiefe verhindert die Umwandlung verschachtelter Objekte in Anzeigezeichenfolgen. Reihenfolge der JSON-Eigenschaften und Einrückung sind unwichtig; die Arrayreihenfolge zählt. Boolesche Werte unterscheiden sich von Zahlen, während gleiche endliche Schreibweisen wie 1 und 1.0 dieselbe JSON-Zahl darstellen.
  • PowerShell kann .NET-Methoden mit [Type]::Method(...) aufrufen. [System.IO.File]::WriteAllText schreibt den ausgewählten Text in die Datei am ausgewählten Pfad. [Environment]::NewLine ergänzt einen Zeilenumbruch, und [System.Text.UTF8Encoding]::new($false) erstellt UTF-8 ohne Byte Order Mark. Diese Methode berücksichtigt WhatIf nicht automatisch; im nächsten Lab stellst du sie hinter einen ausdrücklichen Autorisierungszweig.

Ein wörtlicher Here-String speichert das vollständige Skript: @' öffnet ihn und '@ schließt ihn auf einer eigenen Zeile. Einfache Anführungszeichen erhalten beim Speichern die Variablen des Skripts. Set-Content -Encoding utf8 ersetzt nur diese Skriptdatei:

Set-Content -Path ./Build-RoomPlan.ps1 -Encoding utf8 -Value @'
[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$EventPath,
    [Parameter(Mandatory)][string]$PlanPath,
    [Parameter(Mandatory)][string]$OutputPath
)
try {
    $ErrorActionPreference = 'Stop'
    $fullOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
    if (Get-Item -LiteralPath $fullOutput -Force -ErrorAction SilentlyContinue) {
        throw 'Output already exists; choose a new path.'
    }
    $parent = Split-Path -LiteralPath $fullOutput
    if (-not (Test-Path -LiteralPath $parent -PathType Container)) {
        throw 'Output parent must already exist.'
    }
    $event = Get-Content -LiteralPath $EventPath -Raw | ConvertFrom-Json -AsHashtable -DateKind String
    $requests = Get-Content -LiteralPath $PlanPath -Raw | ConvertFrom-Json -AsHashtable -DateKind String -NoEnumerate
    if ($requests -isnot [array] -or $requests.Count -eq 0) { throw 'Plan must be a nonempty array.' }
    $changes = @()
    $seen = @()
    foreach ($request in $requests) {
        if ($seen -ccontains $request.SessionId) { throw "Duplicate session ID: $($request.SessionId)" }
        $seen += $request.SessionId
        $sessions = @($event.Sessions | Where-Object { $_.SessionId -ceq $request.SessionId })
        $rooms = @($event.Rooms | Where-Object { $_.RoomId -ceq $request.RoomId })
        if ($sessions.Count -ne 1) { throw "Unknown or ambiguous session ID: $($request.SessionId)" }
        if ($rooms.Count -ne 1) { throw "Unknown or ambiguous room ID: $($request.RoomId)" }
        if ($event.ApprovedSessionIds -cnotcontains $request.SessionId) { throw "Unapproved session ID: $($request.SessionId)" }
        if ($sessions[0].Attendees -gt $rooms[0].Capacity) { throw "Capacity exceeded: $($request.SessionId) -> $($request.RoomId)" }
        $changes += [pscustomobject]@{
            SessionId = $request.SessionId
            OldRoomId = $sessions[0].RoomId
            NewRoomId = $request.RoomId
            Attendees = $sessions[0].Attendees
            Capacity = $rooms[0].Capacity
        }
    }
    $json = ConvertTo-Json -InputObject $changes -Depth 5
    [System.IO.File]::WriteAllText($fullOutput, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
    Write-Output "Saved plan: $fullOutput"
}
catch {
    Write-Error ("Plan failed: {0}" -f $_.Exception.Message) -ErrorAction Continue
    exit 1
}
'@

Führe den Validator in einem PowerShell-Kindprozess aus. -NonInteractive verbietet unbeaufsichtigte Rückfragen, und -File wählt das Skript. Das exit 1 des Kindprozesses kann deine interaktive Sitzung nicht schließen:

pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./validated-plan.json

$LASTEXITCODE ist der native Exitstatus des letzten externen Programms. Lies ihn sofort:

$LASTEXITCODE
0

Öffne das tatsächliche Ergebnis erneut:

Get-Content ./validated-plan.json -Raw

Es enthält beide angeforderten Zeilen: s-a, r-old, r-equal, 24, 24; und s-c, r-old, r-large, 18, 40. Der ursprüngliche Zeitplan bleibt unverändert. Eine bestehende Ausgabe wird bewusst abgelehnt, statt stillschweigend ersetzt. Wenn du deinen Plan neu erstellen musst, entferne nur validated-plan.json mit Remove-Item -LiteralPath ./validated-plan.json und führe den korrigierten Validator erneut aus.

Eine spätere ungültige Anfrage vollständig ablehnen

bad-request.json enthält eine erste gültige Änderung, gefolgt von einem unbekannten Zielraum. Der nächste Befehl schlägt absichtlich fehl:

pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./bad-request.json -OutputPath ./bad-plan-result.json

Erwarte Plan failed: und eine Erklärung zum unbekannten Raum. Lies sofort das native Ergebnis:

$LASTEXITCODE
1
Test-Path ./bad-plan-result.json
False

Die frühere gültige Zeile wurde nicht als Teilplan veröffentlicht. Unbekannte Sitzungs-IDs, nicht genehmigte IDs, Duplikate und überschrittene Kapazitäten müssen den Aufruf ebenfalls vor dem Schreiben ablehnen. Eine leere oder fehlende Anfragedatei ist kein erfolgreich erledigter leerer Auftrag.

Prüfe bei einem gewöhnlichen Tippfehler im Pfad mit Get-Location und Get-ChildItem dein Verzeichnis und die Namen und kehre mit Set-Location /home/labex/project/room-changes zurück. Speichere bei einem Skript-Tippfehler den vollständigen korrigierten Here-String erneut und ersetze nur dein Skript. Bearbeite die Quelldaten nicht, um einen Fehler verschwinden zu lassen. Die Abschlussprüfung liest den tatsächlichen fachlichen Plan und die ursprünglichen Eigenschaften und führt deine exakten Skriptbytes in isolierten Ereignissen mit anderen IDs aus. Sie akzeptiert keine feste Liste anfänglicher Antworten und ändert deinen echten Zeitplan nicht.

Zusammenfassung

Du hast eine ehrliche native Vorschau verwendet, Sitzungen über exakte IDs mit Räumen verknüpft, Genehmigung und Kapazität geprüft und einen vollständigen validierten fachlichen Plan gespeichert. Kapazitätsgleichheit wurde akzeptiert, gleichnamige Datensätze blieben getrennt, und eine spätere ungültige Anfrage erzeugte keinen Teilplan. Das ursprüngliche Ereignis und die Notizen blieben unverändert; das nächste Lab wendet einen vollständigen geprüften Plan durch native Bestätigung an.