Introduction
Field Notes a approuvé deux changements de salle pour des ateliers après une hausse de la participation. Plusieurs titres de session et noms de salle se répètent : un plan sûr nécessite donc des identités exactes, un périmètre approuvé et une capacité cible suffisante.
Vous prévisualiserez une petite modification fonctionnelle, puis créerez un validateur de plan PowerShell réutilisable. Ce laboratoire suppose les connaissances en objets, JSON, filtrage, paramètres et gestion des erreurs de PowerShell for Beginners ; aucun script Bash n'est requis. Les outils et les données synthétiques originales de l'événement sont préparés dans un espace de travail indépendant. La durée est une estimation de l'auteur comprenant la lecture et les erreurs ordinaires.
Construire un plan complet fondé sur les ID
Dans cette étape, vous prévisualiserez une modification préparée, puis créerez un validateur réutilisable qui enregistre le plan métier complet sans modifier le programme.
Entrer dans PowerShell et prévisualiser une demande
Ouvrez Terminal. Il exécute initialement un shell Linux. La commande suivante définit UTF-8 uniquement pour cette session PowerShell ; elle ne modifie pas votre shell global. env transmet ces valeurs d'environnement au programme. -NoLogo masque la bannière et -NoProfile ignore les scripts de démarrage personnels :
env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pwsh -NoLogo -NoProfile
Attendez l'invite commençant par PS. Toutes les commandes suivantes s'exécutent dans PowerShell. Set-Location change son répertoire de travail :
Set-Location /home/labex/project/room-changes
Le script préparé preview-one.ps1 traite uniquement la demande de répétition fournie. Il prend en charge le paramètre natif -WhatIf de PowerShell : décrire une action au lieu de l'effectuer. Il s'agit d'un petit exemple fonctionnel, pas encore de votre validateur réutilisable pour plusieurs demandes. ./ désigne un script du répertoire courant :
./preview-one.ps1 -WhatIf
Vous devez obtenir un message natif contenant Move s-a: r-old -> r-equal et la cible ./rehearsal-only.json. Les ID identifient la session, son ancienne salle et la salle proposée. Le script n'a pas déplacé toutes les sessions portant ce titre. Lisez le véritable JSON source avec Get-Content -Raw, qui renvoie le fichier entier sous forme d'une seule chaîne :
Get-Content ./event.json -Raw
L'enregistrement s-a possède toujours un RoomId égal à r-old. Test-Path vérifie l'existence sans rien créer :
Test-Path ./rehearsal-only.json
False
Si vous avez oublié -WhatIf, la répétition peut créer ce fichier distinct, mais elle n'écrase pas l'entrée. Remove-Item -LiteralPath ./rehearsal-only.json supprime uniquement cette sortie de répétition ; utilisez cette commande seulement si le fichier existe, puis recommencez correctement la prévisualisation. -LiteralPath interprète le chemin littéralement sans développer les caractères génériques. Ne supprimez pas l'événement ni les demandes d'origine pour corriger une erreur.
Lire la relation avant de construire l'outil
Le lieu a déjà vérifié que les salles cibles sont libres à ces horaires. Vous vérifiez le périmètre approuvé et la capacité, sans résoudre de conflits de calendrier. Lisez les correspondances demandées et utilisez le lecteur préparé en lecture seule pour consulter le véritable programme d'origine :
Get-Content ./requests.json -Raw
./Show-RoomSchedule.ps1 -Path ./event.json
Les demandes déplacent s-a vers r-equal et s-c vers r-large. s-a et s-b portent le titre Shared maps ; seule s-a est approuvée pour cette demande. Deux salles s'appellent également Studio. Faites les correspondances avec SessionId et RoomId, jamais avec les noms affichés. La première session demandée compte 24 participants et sa salle cible possède exactement 24 places : l'égalité est autorisée.
Vous devez conserver les originaux event.json, requests.json, small-request.json, bad-request.json et le fichier sans rapport notes.txt, y compris leurs octets, mode de fichier, propriétaire et date de modification (mtime). Ne modifiez pas les outils préparés. Vous pouvez créer vos scripts et les nouveaux résultats nommés dans ce laboratoire. Les chemins du cours et toute leur chaîne de répertoires sont de véritables répertoires. Une sortie doit être nouvelle, distincte des entrées, et avoir un parent réel déjà existant ; tout fichier, répertoire ou lien existant, même cassé, doit être refusé sans être touché. Travaillez sans autre écriture concurrente.
Le JSON fourni est bien formé et encodé en UTF-8 : un objet événement contenant Rooms, Sessions et ApprovedSessionIds, et un tableau non vide de demandes avec des ID exacts uniques de sessions et les ID de leurs salles cibles. Les ID sont des chaînes sensibles à la casse. Les nombres de participants et les capacités sont des valeurs entières non négatives. Les métadonnées inconnues peuvent contenir des objets imbriqués, des tableaux ordonnés, des chaînes, des booléens, null et des nombres JSON finis bornés, jusqu'à 20 niveaux d'imbrication. Les clés d'objet exactement dupliquées, les schémas mal formés et les entrées hostiles arbitraires sortent du périmètre du cours.
Valider toutes les demandes avant d'enregistrer un plan
Vous créerez Build-RoomPlan.ps1 avec trois paramètres nommés : EventPath, PlanPath et OutputPath. Il doit fonctionner avec d'autres événements et ID fournis, à l'aide de ces arguments et des commandes installées, sans fichiers auxiliaires voisins. Son résultat est un tableau JSON dans l'ordre des demandes. Chaque ligne contient exactement SessionId, OldRoomId, NewRoomId, Attendees et Capacity. Il s'agit d'un plan métier décrivant les relations actuelles entre objets, pas d'un marqueur affirmant que le travail est terminé. Il ne modifie pas le programme. Le laboratoire suivant écrira un événement complet mis à jour.
Lisez ces ajouts avant d'enregistrer le script :
[CmdletBinding()]en fait un script avancé paramétré ;[Parameter(Mandatory)]exige un argument et[string]déclare du texte.try,catch,throwetexit 1suivent le laboratoire préalable de gestion des erreurs.$ErrorActionPreference = 'Stop'fait arrêter l'opération actuelle par les erreurs de commande. Le bloccatchutiliseWrite-Error -ErrorAction Continuepour afficher l'explication, puis atteindre leexit 1explicite.$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(...)de PowerShell résout la sortie relativement au répertoire PowerShell courant.Split-Path -LiteralPathrenvoie son parent.Get-Item -Forcedécouvre aussi les objets masqués et les liens cassés. Ici,-ErrorAction SilentlyContinueautorise l'absence attendue d'une nouvelle sortie.Test-Path -PathType Containerexige son répertoire parent.ConvertFrom-Json -AsHashtableconserve les objets JSON comme dictionnaires, y compris les clés inconnues qui ne diffèrent que par la casse.-DateKind Stringmaintient les textes ressemblant à des dates sous forme de chaînes et préserve leurs décalages d'origine ; la version préparée de PowerShell prend en charge cette option.-NoEnumerateconserve le tableau de demandes entier même s'il ne contient qu'un élément.@(...)rassemble les correspondances dans un tableau, même s'il y en a zéro ou une.-ceqcompare en respectant la casse.-ccontainsvérifie l'appartenance exacte ;-cnotcontainsest son inverse.-nesignifie différent,-gtsupérieur à et-orindique que l'une des conditions suffit.-isnot [array]refuse une valeur qui n'est pas un tableau.$seen += ...et$changes += ...ajoutent des éléments aux collections. Un ID inconnu, une session non approuvée, une demande dupliquée ou un nombre excessif de participants provoquent une erreur avant toute écriture.-gtautorise une capacité exactement égale au nombre de participants.[pscustomobject]@{...}rassemble les champs connus du plan.ConvertTo-Json -InputObject $changes -Depth 5sérialise le tableau complet ; une profondeur suffisante évite de transformer les objets imbriqués en chaînes d'affichage. L'ordre des propriétés JSON et l'indentation n'importent pas ; l'ordre du tableau compte. Les booléens diffèrent des nombres, tandis que des écritures numériques finies égales telles que1et1.0représentent le même nombre JSON.- PowerShell peut appeler des méthodes .NET avec
[Type]::Method(...).[System.IO.File]::WriteAllTextécrit le texte choisi dans le fichier au chemin sélectionné.[Environment]::NewLineajoute une fin de ligne et[System.Text.UTF8Encoding]::new($false)crée un encodage UTF-8 sans marque d'ordre des octets. Cette méthode ne respecte pas automatiquement WhatIf ; dans le laboratoire suivant, vous la placerez derrière une branche d'autorisation explicite.
Une here-string littérale enregistre le script complet : @' l'ouvre et '@ la ferme sur une ligne à part. Les apostrophes conservent les variables du script lors de l'enregistrement. Set-Content -Encoding utf8 ne remplace que ce fichier de script :
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
}
'@
Exécutez le validateur dans un processus PowerShell enfant. -NonInteractive interdit les questions sans surveillance et -File choisit le script. Le exit 1 de l'enfant ne peut pas fermer votre session interactive :
pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./validated-plan.json
$LASTEXITCODE est le code de sortie natif du dernier programme externe. Lisez-le immédiatement :
$LASTEXITCODE
0
Rouvrez le véritable livrable :
Get-Content ./validated-plan.json -Raw
Il contient les deux lignes demandées : s-a, r-old, r-equal, 24, 24 ; et s-c, r-old, r-large, 18, 40. Le programme d'origine reste inchangé. Une sortie existante est volontairement refusée plutôt que remplacée silencieusement. Pour reconstruire votre plan, supprimez uniquement validated-plan.json avec Remove-Item -LiteralPath ./validated-plan.json, puis relancez le validateur corrigé.
Refuser entièrement une demande ultérieure non valide
bad-request.json contient une première modification valide suivie d'une salle cible inconnue. La commande suivante échoue volontairement :
pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./bad-request.json -OutputPath ./bad-plan-result.json
Attendez Plan failed: et une explication concernant la salle inconnue. Lisez immédiatement le résultat natif :
$LASTEXITCODE
1
Test-Path ./bad-plan-result.json
False
La ligne valide précédente n'a pas été publiée comme plan partiel. Les ID de session inconnus, les ID non approuvés, les doublons et les dépassements de capacité doivent aussi refuser l'invocation avant toute écriture. Un fichier de demandes vide ou absent n'est pas un traitement vide réussi.
Pour une simple faute dans un chemin, utilisez Get-Location et Get-ChildItem afin de vérifier le répertoire et les noms, puis revenez avec Set-Location /home/labex/project/room-changes. Pour une faute dans le script, enregistrez à nouveau la here-string complète corrigée, en ne remplaçant que votre script. Ne modifiez pas les données sources pour faire disparaître une erreur. La vérification finale lit le véritable plan métier et les propriétés d'origine, puis exécute les octets exacts de votre script sur des événements isolés ayant d'autres ID. Elle n'accepte pas une liste fixe de réponses initiales et ne change pas votre programme réel.
Résumé
Vous avez utilisé une prévisualisation native honnête, relié les sessions aux salles par leurs ID exacts, vérifié l'approbation et la capacité, puis enregistré un plan métier validé complet. L'égalité de capacité a été acceptée, les enregistrements de même titre sont restés distincts et une demande ultérieure non valide n'a produit aucun plan partiel. L'événement d'origine et les notes sont restés inchangés ; le laboratoire suivant applique un plan validé entier par confirmation native.



