Введение
После увеличения числа участников Field Notes одобрила две смены комнат для мастер-классов. Несколько названий сессий и комнат повторяются, поэтому безопасному плану нужны точные идентификаторы, разрешённый объём изменений и достаточная вместимость цели.
Вы просмотрите небольшое работающее изменение, а затем создадите многократно используемый валидатор плана на PowerShell. Предполагается знание объектов, JSON, фильтрации, параметров и обработки ошибок из PowerShell for Beginners; сценарии Bash не требуются. Инструменты и исходные синтетические данные мероприятия подготовлены в отдельной независимой рабочей области. Длительность оценена автором с учётом чтения и обычных ошибок.
Создайте полный план на основе ID
На этом шаге вы просмотрите одну подготовленную замену комнаты, а затем создадите многократно используемый валидатор, сохраняющий полный рабочий план без изменения расписания.
Войдите в PowerShell и просмотрите один запрос
Откройте Terminal. Изначально в нём работает оболочка Linux. Следующая команда задаёт UTF-8 только для этого сеанса PowerShell и не меняет глобальную оболочку. env передаёт программе эти значения окружения. -NoLogo скрывает начальный баннер, а -NoProfile пропускает личные сценарии запуска:
env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pwsh -NoLogo -NoProfile
Дождитесь приглашения, начинающегося с PS. Все дальнейшие команды выполняются в PowerShell. Set-Location меняет его рабочий каталог:
Set-Location /home/labex/project/room-changes
Подготовленный preview-one.ps1 обрабатывает только один предоставленный тренировочный запрос. Он поддерживает встроенный параметр PowerShell -WhatIf: описывает действие вместо его выполнения. Это небольшой работающий пример, а не ваш будущий универсальный валидатор нескольких запросов. ./ обозначает сценарий в текущем каталоге:
./preview-one.ps1 -WhatIf
Ожидается встроенное сообщение с Move s-a: r-old -> r-equal и целью ./rehearsal-only.json. ID обозначают сессию, её прежнюю и предлагаемую комнаты. Сценарий не перемещал все сессии с таким названием. Прочитайте настоящий исходный JSON с помощью Get-Content -Raw: команда возвращает весь файл одной строкой:
Get-Content ./event.json -Raw
У записи s-a значение RoomId по-прежнему равно r-old. Test-Path проверяет существование, ничего не создавая:
Test-Path ./rehearsal-only.json
False
Если вы случайно пропустили -WhatIf, тренировка может создать этот отдельный файл, но не перезапишет входные данные. Remove-Item -LiteralPath ./rehearsal-only.json удаляет только тренировочный результат; используйте команду лишь при наличии файла, затем повторите предпросмотр правильно. -LiteralPath воспринимает путь буквально, без раскрытия подстановочных символов. Не удаляйте исходное мероприятие или запросы ради исправления ошибки.
Изучите связи перед созданием инструмента
Площадка уже проверила, что целевые комнаты свободны в это время. Вы проверяете разрешённый объём изменений и вместимость, а не решаете конфликты календаря. Прочитайте запрошенные соответствия и используйте подготовленный инструмент только для чтения, чтобы увидеть настоящее исходное расписание:
Get-Content ./requests.json -Raw
./Show-RoomSchedule.ps1 -Path ./event.json
Запросы перемещают s-a в r-equal, а s-c в r-large. У s-a и s-b одинаковое название Shared maps; для этого запроса одобрена только s-a. Две комнаты также называются Studio. Связывайте записи по SessionId и RoomId, а не по отображаемым названиям. У первой запрошенной сессии 24 участника, а вместимость целевой комнаты ровно 24: равенство допустимо.
Нужно сохранить оригиналы event.json, requests.json, small-request.json, bad-request.json и не относящийся к задаче notes.txt, включая байты, режим файла, владельца и время изменения (mtime). Не редактируйте подготовленные инструменты. Вы можете создавать свои сценарии и новые результаты с указанными в лабораторной работе именами. Пути курса и вся цепочка их каталогов являются настоящими каталогами. Выходной файл должен быть новым, отличаться от входных и иметь уже существующий настоящий родительский каталог; существующий файл, каталог или ссылку, даже с отсутствующей целью, необходимо отклонить без изменений. Работайте без других одновременных записей.
Предоставленный JSON корректен и имеет кодировку UTF-8: объект мероприятия с Rooms, Sessions и ApprovedSessionIds, а также непустой массив запросов с уникальными точными ID сессий и ID целевых комнат. ID — строки с учётом регистра. Числа участников и вместимости — неотрицательные целые значения. Неизвестные метаданные могут содержать вложенные объекты, упорядоченные массивы, строки, логические значения, null и ограниченные конечные числа JSON с вложенностью до 20 уровней. Полностью повторяющиеся ключи объекта, некорректные схемы и произвольные вредоносные входные данные не входят в курс.
Проверьте каждый запрос до сохранения плана
Вы создадите Build-RoomPlan.ps1 с тремя именованными параметрами: EventPath, PlanPath и OutputPath. Он должен работать с другими предоставленными мероприятиями и ID, используя эти аргументы и установленные команды без соседних вспомогательных файлов. Результат — массив JSON в порядке запросов. Каждая строка содержит ровно SessionId, OldRoomId, NewRoomId, Attendees и Capacity. Это рабочий план, описывающий текущие связи объектов, а не отметка о якобы завершённой работе. Расписание он не меняет. В следующей лабораторной работе вы запишете полное обновлённое мероприятие.
Прежде чем сохранять сценарий, изучите дополнения:
[CmdletBinding()]превращает его в расширенный сценарий с параметрами;[Parameter(Mandatory)]требует аргумент, а[string]объявляет текстовый тип.try,catch,throwиexit 1соответствуют предварительной лабораторной работе по обработке ошибок.$ErrorActionPreference = 'Stop'заставляет ошибки команд прерывать текущую операцию. БлокcatchиспользуетWrite-Error -ErrorAction Continue, чтобы показать объяснение, а затем дойти до явногоexit 1.$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(...)в PowerShell разрешает выходной путь относительно текущего каталога PowerShell.Split-Path -LiteralPathвозвращает родительский путь.Get-Item -Forceтакже обнаруживает скрытые объекты и ссылки с отсутствующей целью. Здесь-ErrorAction SilentlyContinueдопускает ожидаемое отсутствие нового результата.Test-Path -PathType Containerтребует родительский каталог.ConvertFrom-Json -AsHashtableсохраняет объекты JSON как словари, включая неизвестные ключи, различающиеся только регистром.-DateKind Stringоставляет похожие на даты строки строками, сохраняя их исходные смещения; подготовленная версия PowerShell поддерживает эту опцию.-NoEnumerateсохраняет массив запросов целиком, даже если в нём один элемент.@(...)собирает совпадения в массив даже при нуле или одном совпадении.-ceqсравнивает с учётом регистра.-ccontainsпроверяет точное вхождение, а-cnotcontains— его противоположность.-neозначает «не равно»,-gt— «больше», а-or— достаточно любого условия.-isnot [array]отклоняет значение, не являющееся массивом.$seen += ...и$changes += ...добавляют элементы в коллекции. Неизвестный ID, неодобренная сессия, повторный запрос или избыток участников вызывают ошибку до записи любого результата.-gtдопускает вместимость, в точности равную числу участников.[pscustomobject]@{...}собирает известные поля плана.ConvertTo-Json -InputObject $changes -Depth 5сериализует весь массив; достаточная глубина не даёт вложенным объектам превратиться в строки для отображения. Порядок свойств JSON и отступы не важны, а порядок массива важен. Логические значения отличаются от чисел, тогда как равные конечные записи, например1и1.0, представляют одно и то же число JSON.- PowerShell может вызывать методы .NET через
[Type]::Method(...).[System.IO.File]::WriteAllTextзаписывает выбранный текст в файл по выбранному пути.[Environment]::NewLineдобавляет перевод строки, а[System.Text.UTF8Encoding]::new($false)создаёт UTF-8 без метки порядка байтов. Этот метод не соблюдает WhatIf автоматически; в следующей работе вы поместите его в явную ветвь авторизации.
Литеральная here-string сохраняет полный сценарий: @' открывает её, а '@ закрывает на отдельной строке. Одинарные кавычки сохраняют переменные сценария при записи. Set-Content -Encoding utf8 заменяет только этот файл сценария:
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
}
'@
Запустите валидатор в дочернем процессе PowerShell. -NonInteractive запрещает вопросы без участия пользователя, а -File выбирает сценарий. Дочерний exit 1 не может закрыть ваш интерактивный сеанс:
pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./validated-plan.json
$LASTEXITCODE — собственный код завершения последней внешней программы. Прочитайте его сразу:
$LASTEXITCODE
0
Снова откройте настоящий результат:
Get-Content ./validated-plan.json -Raw
Он содержит обе запрошенные строки: s-a, r-old, r-equal, 24, 24; и s-c, r-old, r-large, 18, 40. Исходное расписание не изменилось. Существующий результат намеренно отклоняется, а не заменяется без уведомления. Чтобы пересоздать свой план, удалите только validated-plan.json командой Remove-Item -LiteralPath ./validated-plan.json, затем снова запустите исправленный валидатор.
Отклоните весь план с последующим недопустимым запросом
bad-request.json содержит первое допустимое изменение, за которым следует неизвестная целевая комната. Следующая команда намеренно завершится ошибкой:
pwsh -NoLogo -NoProfile -NonInteractive -File ./Build-RoomPlan.ps1 -EventPath ./event.json -PlanPath ./bad-request.json -OutputPath ./bad-plan-result.json
Ожидается Plan failed: и объяснение о неизвестной комнате. Сразу прочитайте собственный код завершения:
$LASTEXITCODE
1
Test-Path ./bad-plan-result.json
False
Первая допустимая строка не была опубликована как частичный план. Неизвестные ID сессий, неодобренные ID, дубликаты и превышение вместимости также должны отклонять вызов до записи. Пустой или отсутствующий файл запросов — не успешно выполненная пустая задача.
При обычной опечатке в пути проверьте каталог и имена через Get-Location и Get-ChildItem, затем вернитесь с помощью Set-Location /home/labex/project/room-changes. При опечатке в сценарии заново сохраните полную исправленную here-string, заменяя только свой сценарий. Не редактируйте исходные данные, чтобы ошибка исчезла. Итоговая проверка читает настоящий рабочий план и исходные свойства и запускает точные байты вашего сценария на изолированных мероприятиях с другими ID. Она не принимает фиксированный список исходных ответов и не меняет ваше реальное расписание.
Резюме
Вы использовали честный встроенный предпросмотр, связали сессии с комнатами по точным ID, проверили одобрение и вместимость и сохранили полный проверенный рабочий план. Равенство вместимости было принято, одноимённые записи остались разными, а поздний неверный запрос не создал частичного плана. Исходное мероприятие и заметки не изменились; в следующей работе вы примените полный проверенный план через встроенное подтверждение.



