Powershell вывод ошибок в файл

Обновлено: 30.06.2024

Командлеты должны передавать объект System. Management. Automation. ерроррекорд , который определяет условие ошибки для завершающих и устранимых ошибок.

  • Исключение, описывающее ошибку. Часто это исключение, которое командлет перехватывает и преобразует в запись об ошибке. Каждая запись об ошибке должна содержать исключение.

Если командлет не перехватывает исключение, он должен создать новое исключение и выбрать класс исключения, который наилучшим образом описывает условие ошибки. Однако исключение не требуется, так как доступ к нему можно получить с помощью свойства System. Management. Automation. ерроррекорд. Exception объекта System. Management. Automation. ерроррекорд .

идентификатор ошибки, предоставляющий целевой указатель, который может использоваться в целях диагностики, а также скрипты Windows PowerShell для обработки определенных условий ошибок с определенными обработчиками ошибок. Каждая запись об ошибке должна содержать идентификатор ошибки (см. Идентификатор ошибки).

Категория ошибок, которая предоставляет общее обозначение, которое можно использовать в целях диагностики. Каждая запись об ошибке должна указывать категорию ошибок (см. раздел Категория ошибок).

Целевой объект, который был обработан при возникновении ошибки. Это может быть входной объект или другой объект, обрабатываемый командлетом. Например, для команды remove-item -recurse c:\somedirectory ошибка может быть экземпляром объекта FileInfo для "к:\сомедиректори\локкедфиле". Сведения о целевом объекте являются необязательными.

Идентификатор ошибки

При создании записи об ошибке укажите идентификатор, обозначающий условие ошибки в командлете. Windows PowerShell объединяет целевой идентификатор с именем командлета, чтобы создать полный идентификатор ошибки. Полный идентификатор ошибки можно получить с помощью свойства System. Management. Automation. ерроррекорд. фулликуалифиедеррорид объекта System. Management. Automation. ерроррекорд . Идентификатор ошибки недоступен для самого себя. Он доступен только в составе полного идентификатора ошибки.

При создании записей об ошибках следуйте приведенным ниже рекомендациям по созданию идентификаторов ошибок.

Создание идентификаторов ошибок, относящихся к условию ошибки. Нацеливание идентификаторов ошибок для целей диагностики и скриптов, которые обрабатывали определенные условия ошибок с помощью конкретных обработчиков ошибок. Пользователь должен иметь возможность использовать идентификатор ошибки для идентификации ошибки и ее источника. Идентификаторы ошибок также позволяют создавать отчеты о конкретных условиях ошибок из существующих исключений, чтобы новые подклассы исключений не требовались.

При публикации кода с использованием определенного идентификатора ошибки вы устанавливаете семантику ошибок с этим идентификатором для полного жизненного цикла поддержки продукта. Не используйте его в контексте, который семантически отличается от исходного контекста. При изменении семантики этой ошибки создайте и затем используйте новый идентификатор.

Обычно конкретный идентификатор ошибки следует использовать только для исключений определенного типа CLR. Если тип исключения или тип целевого объекта изменяется, создайте и используйте новый идентификатор.

Не следует динамически создавать идентификаторы ошибок невоспроизводимым способом. Например, не следует включать сведения об ошибке, такие как идентификатор процесса. Идентификаторы ошибок полезны только в том случае, если они соответствуют идентификаторам ошибок, видимым другим пользователям, которые столкнулись с тем же условием ошибки.

Категория ошибки

При создании записи об ошибке укажите категорию ошибки с помощью одной из констант, определенных перечислением System. Management. Automation. ErrorCategory . Windows PowerShell использует категорию ошибок для вывода сведений об ошибках, когда пользователь присваивает $ErrorView переменной значение "CategoryView" .

Старайтесь не использовать константу System. Management. Automation. ErrorCategory NotSpecified . Если у вас есть какие-либо сведения об ошибке или об операции, вызвавшей ошибку, выберите категорию, которая лучше описывает ошибку или операцию, даже если категория не является идеальным соответствием.

сведения, отображаемые Windows PowerShell, называются строкой представления "категория" и формируются из свойств класса System. Management. Automation. ерроркатегоринфо . (Доступ к этому классу осуществляется через свойство Error System. Management. Automation. ерроррекорд. CategoryInfo .)

В следующем списке описаны отображаемые сведения.

Category: определенная Windows PowerShell константа System. Management. Automation. ErrorCategory .

TargetName: по умолчанию имя объекта, обрабатываемого командлетом при возникновении ошибки. Или другая строка, определяемая командлетом.

TargetType: по умолчанию это тип целевого объекта. Или другая строка, определяемая командлетом.

Действие: по умолчанию имя командлета, создавшего запись об ошибке. Или другая строка, определяемая командлетом.

Причина: по умолчанию это тип исключения. Или другая строка, определяемая командлетом.

ErrorDetails (командлет, строка, строка, объект []). Используйте этот конструктор, если строка шаблона является строкой ресурса в той же сборке, в которой реализуется командлет, или если требуется загрузить строку шаблона с помощью переопределения метода System. Management. Automation. командлета. жетресаурцестринг .

ErrorDetails (сборка, строка, строка, объект []): Используйте этот конструктор, если строка шаблона находится в другой сборке и вы не загрузили ее с помощью переопределения System. Management. Automation. командлета. жетресаурцестринг.

Рекомендуемые сведения о действии

Объект System. Management. Automation. ErrorDetails также может предоставлять сведения о том, какие действия рекомендуются при возникновении ошибки.

Сведения о вызове

Как работать с исключениями и ошибками в Powershell с Try и Catch на примерах

В Powershell существует несколько уровней ошибок и несколько способов их обработать. Проблемы одного уровня (Non-Terminating Errors) можно решить с помощью привычных для Powershell команд. Другой уровень ошибок (Terminating Errors) решается с помощью исключений (Exceptions) стандартного, для большинства языков, блока в виде Try, Catch и Finally.

Навигация по посту

Как Powershell обрабатывает ошибки

До рассмотрения основных методов посмотрим на теоретическую часть.

Автоматические переменные $Error

Переменная $Error в Powershell

При отсутствии каких либо ошибок мы бы получили пустой ответ, а счетчик будет равняться 0:

Счетчик ошибок с переменной $Error в Powershell

Переменная $Error являет массивом и мы можем по нему пройтись или обратиться по индексу что бы найти нужную ошибку:

Вывод ошибки по индексу в Powershell c $Error

Свойства объекта $Error

Так же как и все что создается в Powershell переменная $Error так же имеет свойства (дополнительную информацию) и методы. Названия свойств и методов можно увидеть через команду Get-Member:

Свойства переменной $Error в Powershell

Например, с помощью свойства InvocationInfo, мы можем вывести более структурный отчет об ошибки:

Детальная информация об ошибке с $Error в Powershell

Методы объекта $Error

Например мы можем очистить логи ошибок используя clear:

Очистка логов об ошибке в Powershell с $Error

Критические ошибки (Terminating Errors)

Критические ошибки в Powershell Terminating Errors

Не критические ошибки (Non-Terminating Errors)

Не критические ошибки в Powershell Non-Terminating Errors

Параметр ErrorVariable

Если вы не хотите использовать автоматическую переменную $Error, то сможете определять свою переменную индивидуально для каждой команды. Эта переменная определяется в параметре ErrorVariable:

Использование ErrorVariable в Powershell

Переменная будет иметь те же свойства, что и автоматическая:

Свойства ErrorVariable в Powershell

Повторное использование логина и пароля в Powershell с Get-Credential и их шифрование

Обработка некритических ошибок

Приоритет ошибок с $ErrorActionPreference

Определение $ErrorActionPreference в Powershell

Определение глобальной переменной $ErrorActionPreference в Powershell

Т.е. скрипт был остановлен в самом начале. Значение переменной будет храниться до момента завершения сессии Powershell. При перезагрузке компьютера, например, вернется значение по умолчанию.

Ниже значение, которые мы можем установить в переменной $ErrorActionPreference:

  • Continue - вывод ошибки и продолжение работы;
  • Inquire - приостановит работу скрипта и спросит о дальнейших действиях;
  • SilentlyContinue - скрипт продолжит свою работу без вывода ошибок;
  • Stop - остановка скрипта при первой ошибке.

Самый частый параметр, который мне приходится использовать - SilentlyContinue:

Игнорирование ошибок в Powershell с ErrorActionPreference и SilentlyContinue

Использование параметра ErrorAction

Переменная $ErrorActionPreference указывает глобальный приоритет, но мы можем определить такую логику в рамках команды с параметром ErrorAction. Этот параметр имеет больший приоритет чем $ErrorActionPreference. В следующем примере, глобальная переменная определяет полную остановку скрипта, а в параметр ErrorAction говорит "не выводить ошибок и продолжить работу":

Использование параметра ErrorAction в ошибках с Powershell

Значение Stop, в обоих случаях, делает ошибку критической.

Обработка критических ошибок и исключений с Try, Catch и Finally

Когда мы ожидаем получить какую-то ошибку и добавить логику нужно использовать Try и Catch. Например, если в вариантах выше мы определяли нужно ли нам отображать ошибку или останавливать скрипт, то теперь сможем изменить выполнение скрипта или команды вообще. Блок Try и Catch работает только с критическими ошибками и в случаях если $ErrorActionPreference или ErrorAction имеют значение Stop.

Например, если с помощью Powershell мы пытаемся подключиться к множеству компьютеров один из них может быть выключен - это приведет к ошибке. Так как эту ситуацию мы можем предвидеть, то мы можем обработать ее. Процесс обработки ошибок называется исключением (Exception).

Синтаксис и логика работы команды следующая:

Блок try мониторит ошибки и если она произойдет, то она добавится в переменную $Error и скрипт перейдет к блоку Catch. Так как ошибки могут быть разные (нет доступа, нет сети, блокирует правило фаервола и т.д.) то мы можем прописывать один блок Try и несколько Catch:

Сам блок finally - не обязательный и используется редко. Он выполняется самым последним, после try и catch и не имеет каких-то условий.

Catch для всех типов исключений

Как и было показано выше мы можем использовать блок Catch для конкретного типа ошибок, например при проблемах с доступом. Если в этом месте ничего не указывать - в этом блоке будут обрабатываться все варианты ошибок:

Игнорирование всех ошибок с try и catch в Powershell

Такой подход не рекомендуется использовать часто, так как вы можете пропустить что-то важное.

Мы можем вывести в блоке catch текст ошибки используя $PSItem.Exception:

Переменная PSITem в блоке try и catch в Powershell

Переменная $PSItem хранит информацию о текущей ошибке, а глобальная переменная $Error будет хранит информацию обо всех ошибках. Так, например, я выведу одну и ту же информацию:

Создание отдельных исключений

Что бы обработать отдельную ошибку сначала нужно найти ее имя. Это имя можно увидеть при получении свойств и методов у значения переменной $Error:

Поиск имени для исключения ошибки в Powershell

Так же сработает и в блоке Catch с $PSItem:

Наименование ошибок для исключений в Powershell

Для вывода только имени можно использовать свойство FullName:

Вывод типа ошибок и их названия в Powershell

Далее, это имя, мы вставляем в блок Catch:

Указываем исключение ошибки в блоке Try Catch Powershell

Так же, как и было описано выше мы можем усложнять эти блоки как угодно указывая множество исключений в одном catch.

Создание и изменение в Powershell NTFS разрешений ACL

Выброс своих исключений

Иногда нужно создать свои собственные исключения. Например мы можем запретить добавлять через какой-то скрипт названия содержащие маленькие буквы или сотрудников без указания возраста и т.д. Способов создать такие ошибки - два и они тоже делятся на критические и обычные.

Выброс с throw

Throw - выбрасывает ошибку, которая останавливает работу скрипта. Этот тип ошибок относится к критическим. Например мы можем указать только текст для дополнительной информации:

Выброс ошибки с throw в Powershell

Если нужно, то мы можем использовать исключения, которые уже были созданы в Powershell:

Выброс ошибки с throw в Powershell

Использование Write-Error

Команда Write-Error работает так же, как и ключ ErrorAction. Мы можем просто отобразить какую-то ошибку и продолжить выполнение скрипта:

Использование Write-Error для работы с исключениями в Powershell

Использование Write-Error и Stop в Powershell

Отличие команды Write-Error с ключом ErrorAction от обычных команд в том, что мы можем указывать исключения в параметре Exception:

Свойства Write-Errror в Powershell

Как работает конвейер в Powershell и что такое pipeline на примерах

Ключевой особенностью Powershell, которая отличает его от других языков, это работа с конвейером. Дело в том, что каждая команда в Powershell возвращает множество объектов, а не один объект типа строка. Такой метод дает дополнительные возможности для работы с языком. Конвейер так же называют pipe или pipeline. В это статье будут рассмотрены примеры работы конвейера, его сравнение с bash, отличие от циклов и создание функции принимающей данные с конвейера.

Навигация по посту

Как работает конвейер

Если бы мы использовали конвейер с буквами, то получилось бы следующее:

В примере ниже слева указана строка, которая выводится через команду справа:

Предыдущий пример идентичен, по функциональности и результату, следующему выполнению:

Работа конвейера и обычно команды с параметрами Powershell

Суть в том, что мы можем указать несколько значений в левой части. Каждое из этих значений будет передаваться асинхронно (т.е. отдельно). Значения, которые разделяются запятыми в Powershell, образуют массив:

В командах без конвейера мы редко можем использовать массивы в качестве значений. Что бы получить результат аналогичный предыдущей команде мы должны вызвать ее дважды:

Передача массивов через конвейер Powershell

Так мы получим только день из сегодняшней даты:

stdin, stdout и stderr

Если вам сложно понять работу конвейера с примеров выше вы можете ознакомиться с понятием потоков (streams). Потоки реализованы в *nix системах и Windows. В Powershell реализовано 7 потоков, но основная концепция понятна в 3 потоках, которые так же реализованы в *nix системах. Это:

  • stdin - ввод данных. Этот поток используется, когда вы печатаете текст или, например, когда результат работы одной команды принимает следующая. На уровне Powershell этого потока нет (в документациях не указывается, но скорее всего он реализован на уровне ОС);
  • stdout - вывод результата. Этот результат может быть направлен на экран консоли, в файл, в другую команду и т.д. В powershell этот поток называется "Success Stream";
  • stderr - вывод ошибок. В Powershell называется "Error Stream".

Пример работы этих потоков в обычном виде:

Работа stdin и stdout в Powershell

В 1-ом случае мы используем stdin т.к. напечатали команду с клавиатуры. После вызова команды мы получили stdout (2). При ошибках мы получаем stderr.

В конвейере работает такой же принцип:

Работа stdin и stdout в конвейере Powershell

Разница конвейеров bash и Powershell

Разница работы конвейеров в типе объектов, которые через них проходят. В Powershell, в подавляющем большинстве, возвращается (попадают в stdout) массивы следующего вида:

Благодаря этому мы можем сразу получать нужное свойство не использовав сложные конструкции и регулярные выражения:

Передача массивов ключ-значение через конвейер Powershell

Bash же отправляет в stdout информацию в виде строки:

Так как это сплошной текст - нам приходится использовать дополнительные механизмы для поиска нужных данных. Например grep:

Сравнение конвейера Powershell и Bash

Есть и другие отличия. Например в Powershell есть переменные, в которые и помещаются данные конвейера: $PSItem или $_. Часть из таких возможностей будет рассмотрена далее.

Различия с циклом

Пайп схож с циклами, но более автоматизирован. Что бы с помощью цикла получить название дисков, а затем изменить цвет вывода, мы должны сделать следующее:

Сравнение цикла и пайплайна в Powershell

У нас использован совершенно обычный цикл, который есть во всех языках. Мы объявляем переменную "$item", в которую помещается временное значение. Это значение мы отдельно выводим. Т.е. мы выполняем 3 действия:

  1. Объявление цикла;
  2. Объявление переменной с текущим значением;
  3. Вывод переменной (или другие действия, например сохранение данных в файл).

В Powershell реализован так же команда-цикл "ForEach-Object". Особенность этой команды в том, что используется только часть возможности конвейера и часть от обычного цикла:

Сравнение ForEach-Object и пайплайна в Powershell

Как можно увидеть с примера выше у нас исчез шаг под номером 2. Мы больше не объявляем временную переменную - она формируется автоматически под именем "PSItem". Эта переменная появляется во время передачи данных через pipeline. В нее помещается результат работы "Get-PSDrive".

Конвейер убирает еще, как минимум, один шаг. Нам не нужно объявлять цикл - мы просто вызываем переменную:

Используем PSItem в конвейере Powershell

В некоторых случаях мы можем не указывать "PSItem" вовсе. Обычно это команды одного типа, например, работы с сервисом:

Как принимаются данные с конвейера

Если мы выполним следующую команду, то увидим, что у нее есть множество свойств (левая колонка), которые мы можем вернуть:

Свойства, которые могут быть переданы через конвейер

Вопрос в том, как конвейер узнает, что нам нужно использовать свойство "Name" в "Restart-Service"? В одной команде множество свойств и значений, а другой множество параметров и они могут не совпадать. У нас так же не возвращаются какие-либо ошибки.

Что бы понять как будут распределены свойства и значения нам нужно посмотреть справку по этой команде:

Какой параметр используется конвейером Powershell

  • ByPropertyName - по свойству (должен быть использован массив ключ-значение);
  • ByValue - по значению (одномерный массив).

Как передаются массивы через конвейер Powershell

Как передаются массивы ключ-значение через конвейер Powershell

Не все команды возвращают PSCustomObject. Это говорит о том, что вы можете использовать и другие типы объектов:

Типы массивов ключ-значение в Powershell

Главный принцип, по которому значение может таким образом успешно попасть в конвейер, это быть итерируемым. В hashtable такого по умолчанию нет.

hashtable

Не все объекты Powershell могут проходить через конвейер. Как написано в документации у всех типов Powershell, кроме hashtable, есть поддержка работы через конвейеры (IEnumerable). При попытке пропустить хэш-таблицу через конвейер возникнет ошибка:

  • Get-Service : Не удается найти службу с именем службы "System.Collections.Hashtable".

Передача hastable через конвейер Powershell

Передача hastable через конвейер Powershell с GetEnumerator

Еще два способа получить только ключи или только значения:

Другие возможности получения значений из хэш-таблиц Powershell

Создание функции работающей через Pipeline

Еще один способ понять работу конвейера - это создать собственную команду (функцию). Мы можем объявить два параметра, которые могут быть использованы вместе, так и отдельно:

Эти параметры мы можем совмещать, так и указывать отдельно:

Создание функции принимающей массив через конвейер Powershell

В отличие от предыдущего способа, ValueFromPipelineByPropertyName мы можем использовать множество раз. Самое главное использовать правильные названия переменных и ключей:

Создание функции принимающей массив ключ-значение через конвейер Powershell

Мы можем комбинировать параметры ValueFromPipelineByPropertyName и ValueFromPipeline. Я бы рекомендовал избегать таких способов т.к. в них легко запутаться:

Совмещение параметров работающие с конвейером Powershell

Обратите внимание на помеченные фрагменты. Мы должны определять тип данных, который ожидается (случай 1 и 2) иначе конвейер не будет обрабатывать некоторые данные корректно. Так же, в случае 3, у нас выводится только последний элемент. Это происходит из-за отсутствия Process.

Блок Process

При создании функций в Powershell вы можете использовать блоки: begin, process и end. В большинстве случаев это не требуется и вы можете писать функцию по принципам описанным выше. В случае конвейеров эти блоки приобретают дополнительный смысл:

  • begin - значение, переданное через конвейер, этому блоку не известно. Оно будет равно $null либо другому значению по умолчанию, которое вы установите;
  • process - обрабатывает каждый элемент переданные через конвейер отдельно;
  • end - обрабатывает только последний элемент переданный через конвейер. Если вы не указывали какие-то блоки, как в примере выше, по умолчанию используется именно этот блок. Из-за этого выводится только последний элемент массива.

Пример работы всех блоков:

Создание команды Powershell с begin end и process

Там, где использовался конвейер, появился 0. Это связано с тем, что блоку begin не известна эта переменна, т.е. это значение равно $Null. Т.к. эта переменная должна хранить число, а не строку, этот тип преобразовывается в 0:


Если для вас это слишком сложная конструкция, то вы можете использовать только блок Process. В случае обычных использований команды он вызывается единожды, а в случае конвейера - для каждого значения отдельно. Важно отметить, что вы не должны писать код вне Process:

Оптимизация создание функции с блоком process Powershell

Массивы

Вы наверняка использовали команды, которые позволяют использовать следующую конструкцию:

Используя примеры выше у вас не получится организовать такую возможность. У вас появится ошибка в первом случае т.к. ожидается число, а вы передаете массив:

Создание функции Powershell принимающие массив через конвейер

  • Не удается обработать преобразование аргументов для параметра "param". Не удается преобразовать значение.

Создание функции Powershell принимающие массив через конвейер

Если вам нужно передавать именованные параметры, то нужно будет организовывать дополнительные проверки. Я не рекомендую использовать такие сложные конструкции.

Асинхронное выполнение

Конвейер Powershell передает значения асинхронно. Это значит, что несколько функций будут работать друг с другом еще до завершения своей работы. Это так же может быть важно в отдельных случаях.

В примере ниже видно, что мы передаем значения из массива постепенно, а не целиком. У нас чередуется выполнение функций (a,b,a. ):

Асинхронное выполнение конвейера в Powershell

Ошибки

При создании конвейеров легко запутаться и получить странную ошибку. Часть из них рассмотрена ниже.

  • Не удается привязать объект ввода к любым параметрам команды, так как команда не принимает входные данные конвейера, либо входные данные и их свойства не совпадают с любыми из параметров, принимающих входные данные конвейера;
  • The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.

Ошибки при работе с конвейером Powershell

Если вы укажете код вне блока Process, то так же получите странную ошибку:

Объясняется, как перенаправлять выходные данные из PowerShell в текстовые файлы.

Подробное описание

Для перенаправления выходных данных можно использовать следующие методы:

Используйте Out-File командлет, который отправляет выходные данные команды в текстовый файл. Как правило, командлет используется, Out-File когда необходимо использовать параметры, такие как Encoding ,, Force Width или NoClobber .

Используйте Tee-Object командлет, который отправляет выходные данные команды в текстовый файл и отправляет его в конвейер.

Используйте операторы перенаправления PowerShell. Использование оператора перенаправления с целевым объектом файла функционально эквивалентно конвейеру Out-File без дополнительных параметров.

Дополнительные сведения о потоках см. в разделе about_Output_Streams.

Перенаправляемые выходные потоки

PowerShell поддерживает перенаправление следующих выходных потоков.

В PowerShell также присутствует поток хода выполнения , но он не поддерживает перенаправление.

Потоки успехов и ошибок похожи на потоки stdout и stderr других оболочек. Однако stdin не подключен к конвейеру PowerShell для ввода данных.

Операторы перенаправления PowerShell

Ниже приведены операторы перенаправления PowerShell, где n представляет номер потока. Если поток не указан, по умолчанию используется поток Success ( 1 ).

Примеры

Пример 1. ошибки перенаправления и выходные данные в файл

Этот пример выполняется dir на одном элементе, который будет выполнен, и один из которых будет иметь ошибку.

Он использует 2>&1 для перенаправления потока ошибок в поток успешного выполнения и > отправки результирующего потока успешного выполнения в файл с именем. dir.log

Пример 2. Отправка всех успешных данных потока в файл

В этом примере все успешные потоковые данные отправляются в файл с именем script.log .

Пример 3. Передача в файл успешных, предупреждающих и ошибочных потоков

В этом примере показано, как можно комбинировать операторы перенаправления для достижения желаемого результата.

  • 3>&1 перенаправляет поток предупреждений в поток успешного выполнения .
  • 2>&1 перенаправляет поток ошибок в поток успешного выполнения (который также включает все данные потока предупреждений ).
  • > перенаправляет поток успешного выполнения (который теперь содержит потоки предупреждений и ошибок ) в файл с именем C:\temp\redirection.log ).

Пример 4. перенаправление всех потоков в файл

Этот пример отправляет все выходные данные из скрипта, вызываемого script.ps1 в файл с именем script.log

Пример 5. Отключение всех данных Write-Host и информационных потоков

В этом примере подавляются все данные потока информации. Дополнительные сведения о командлетах информационного потока см. в статье Write-Host и Write-Information .

Пример 6. Отображение влияния настроек действий

Переменные и параметры настройки действия могут изменять содержимое, записываемое в определенный поток. Скрипт в этом примере показывает, как значение влияет на то, $ErrorActionPreference что записывается в поток ошибок .

При выполнении этого скрипта выводится запрос, когда $ErrorActionPreference для параметра задано значение Inquire .

При изучении файла журнала мы увидим следующее:

Примечания

Операторы перенаправления, которые не добавляют данные ( > и n> ) перезаписывают текущее содержимое указанного файла без предупреждения.

Однако если файл доступен только для чтения, является скрытым или системным файлом, перенаправление завершается ошибкой. Операторы добавления перенаправления ( >> и n>> ) не записывают в файл, доступный только для чтения, но добавляют содержимое в системный или скрытый файл.

Для принудительного перенаправления содержимого в скрытый или системный файл только для чтения используйте Out-File командлет со своим Force параметром.

При записи в файлы операторы перенаправления используют UTF8NoBOM кодировку. Если файл имеет другую кодировку, выходные данные могут быть неправильно отформатированы. Для записи в файлы с другой кодировкой используйте Out-File командлет с его Encoding параметром.

Ширина выходных данных при записи в файл

При записи в файл с помощью Out-File или операторов перенаправления PowerShell преобразует выходные данные таблицы в файл на основе ширины консоли, в которой она выполняется. Например, при записи выходных данных таблицы в файл с помощью команды, например Get-ChildItem Env:\Path > path.log в системе, в которой ширина консоли равна 80 столбцам, выходные данные в файле усекаются до 80 символов:

Учитывая, что ширина консоли может быть задана произвольным образом на системах, где выполняется сценарий, вы можете предпочесть, чтобы формат PowerShell был выводить файлы таблицы в соответствии с заданной заданной шириной.

Out-File Командлет предоставляет параметр Width , позволяющий задать ширину для вывода в таблицу. Вместо того, чтобы добавлять -Width 2000 все при вызове Out-File , можно использовать $PSDefaultParameterValues переменную, чтобы задать это значение для всех использований Out-File командлета в скрипте. А поскольку операторы перенаправления ( > и >> ) фактически являются псевдонимами для Out-File , установка Out-File:Width параметра для всего скрипта влияет также на ширину форматирования для операторов перенаправления. Добавьте следующую команду в начало скрипта, чтобы задать Out-File:Width для всего скрипта:

Увеличение ширины выходных данных приведет к увеличению потребления памяти при регистрации выходных данных в табличном формате. Если вы записываете в файл большой объем табличных данных и вы уверены, что можете сделать это с меньшей шириной, используйте меньшую ширину.

В некоторых случаях, например Get-Service в выходных данных, чтобы использовать дополнительную ширину, необходимо передать выходные данные перед выводом Format-Table -AutoSize в файл.

Дополнительные сведения о $PSDefaultParameterValues см. в разделе about_Preference_Variables.

Потенциальная путаница с операторами сравнения

> Оператор не следует путать с оператором сравнения " больше " (часто обозначается как > в других языках программирования).

В зависимости от сравниваемых объектов выходные данные > могут выглядеть правильными (поскольку 36 не превышает 42).

Однако проверка локальной файловой системы может видеть, что файл 42 с именем был записан с содержимым 36 .

Попытка использовать обратные сравнения < (меньше) приводит к системной ошибке:

Значение, если числовое сравнение является обязательной операцией -lt и -gt должно использоваться. Дополнительные сведения см. в описании -gt оператора в about_Comparison_Operators.

Обработка ошибок — это лишь часть процесса написания кода. Зачастую можно проверить, выполняются ли условия для ожидаемого поведения. При возникновении непредвиденной ситуации необходимо переключиться на обработку исключений. Вы можете легко обрабатывать исключения, созданные кодом других разработчиков, или создавать собственные исключения, которые смогут обработать другие.

Базовая терминология

Прежде чем двигаться дальше, необходимо разобраться с основными терминами.

Исключение

Throw и Catch

Когда происходит исключение, мы говорим, что оно вызвано. Чтобы обработать вызванное исключение, его необходимо перехватить. Если вызывается исключение, которое не перехватывается никакими объектами, выполнение скрипта прекращается.

Стек вызовов

Стек вызовов — это список функций, которые вызывали друг друга. При вызове функции она добавляется в стек или вверх списка. При выходе из функции или возврате ею значения она удаляется из стека.

При вызове исключения выполняется проверка этого стека вызовов, чтобы обработчик исключений мог его перехватить.

Неустранимые и устранимые ошибки

Исключение обычно является неустранимой ошибкой. Вызванное исключение либо перехватывается, либо завершает текущее выполнение. По умолчанию устранимая ошибка генерируется Write-Error и приводит к добавлению ошибки в выходной поток без вызова исключения.

Обращаю внимание на это потому, что Write-Error и другие устранимые ошибки не активируют catch .

Игнорирование исключения

Это ситуация, когда ошибка перехватывается только для того, чтобы ее подавить. Используйте эту возможность с осторожностью, поскольку это может существенно усложнить устранение неполадок.

Основной синтаксис команды

Ниже приведен краткий обзор основного синтаксиса обработки исключений, используемого в PowerShell.

Ключевое слово throw

Чтобы вызвать собственное событие исключения, воспользуйтесь ключевым словом throw .

Параметр -ErrorAction Stop командлета Write-Error

Я упоминал, что по умолчанию Write-Error не вызывает неустранимую ошибку. Если указать -ErrorAction Stop , то Write-Error создает неустранимую ошибку, которую можно обработать с помощью catch .

Благодарю Ли Дейли за напоминание о том, что -ErrorAction Stop можно использовать таким образом.

Параметр -ErrorAction Stop в командлете

Если указать -ErrorAction Stop в любой расширенной функции или командлете, все инструкции Write-Error будут преобразованы в неустранимые ошибки, которые приводят к остановке выполнения или могут быть обработаны с помощью catch .

Try и Catch

Принцип обработки исключений в PowerShell (и многих других языках) состоит в том, что сначала к разделу кода применяется try , а если происходит ошибка, к нему применяется catch . Приведем краткий пример.

Скрипт catch выполняется только в том случае, если произошла неустранимая ошибка. Если try выполняется правильно, catch пропускается. Можно получить доступ к информации об исключении в блоке catch с помощью переменной $_ .

Try и Finally

Иногда ошибку обрабатывать не требуется, но необходимо выполнить определенный код в зависимости от того, было ли создано исключение. Именно это и делает скрипт finally .

Взгляните на этот пример.

Всякий раз, когда вы открываете ресурс или подключаетесь к нему, его следует закрыть. Если ExecuteNonQuery() вызывает исключение, соединение не закрывается. Вот тот же код в блоке try/finally .

В этом примере соединение закрывается при возникновении ошибки. Оно также закрывается при отсутствии ошибок. При этом всякий раз выполняется скрипт finally .

Так как исключение не перехватывается, оно по-прежнему распространяет в стек вызовов.

Try, catch и finally

Вполне допустимо использовать catch и finally вместе. В большинстве случаев используется либо один, либо другой скрипт, но вам могут встретиться сценарии, в которых используются они оба.

$PSItem

Теперь, когда мы разобрались с основами, можно изучить вопрос подробнее.

В блоке catch существует автоматическая переменная ( $PSItem или $_ ) типа ErrorRecord , содержащая сведения об исключении. Ниже приведен краткий обзор некоторых ключевых свойств.

В этих примерах для создания такого исключения я использовал недопустимый путь в ReadAllText .

PSItem.ToString()

$PSItem.InvocationInfo

Это свойство содержит дополнительные сведения, собираемые PowerShell о функции или скрипте, которые вызвали исключение. Ниже приведено свойство InvocationInfo из примера созданного мною исключения.

Здесь приведены важные сведения: имя ScriptName , строка кода Line и номер строки ScriptLineNumber , из которой инициирован вызов.

$PSItem.ScriptStackTrace

Это свойство показывает порядок вызовов функций, ведущих к коду, в котором создано исключение.

Я выполняю вызовы функций в пределах одного скрипта, но таким же образом можно отслеживать функции при использовании нескольких скриптов.

$PSItem.Exception

Это, собственно, и есть вызванное исключение.

$PSItem.Exception.Message

$PSItem.Exception.InnerException

Исключения могут содержать внутренние исключения. Это часто случается, когда вызываемый код перехватывает одно исключение и вызывает другое. Исходное исключение помещается в новое исключение.

Я вернусь к этому вопросу позже, когда буду рассказывать о повторном вызове исключений.

$PSItem.Exception.StackTrace

Это свойство StackTrace для исключения. Я продемонстрировал принцип работы свойства ScriptStackTrace выше, но это предназначено для вызовов управляемого кода.

Работа с исключениями

Для работы с исключениями недостаточно базового синтаксиса и основных свойств.

Перехват типизированных исключений

Исключения можно перехватывать избирательно. Исключения имеют тип, задав который можно перехватить только исключения определенного типа.

Каждый блок catch проверяется на наличие исключения заданного типа, пока не будет найден тот, в котором оно создается. Важно понимать, что исключения могут наследоваться от других исключений. В приведенном выше примере FileNotFoundException наследуется от IOException . Поэтому, если исключение IOException было первым, будет вызвано именно оно. Даже если совпадений несколько, вызывается только один блок catch.

При наличии исключения System.IO.PathTooLongException исключение IOException распознается как совпадение, но при наличии исключения InsufficientMemoryException перехватывать его нечем, поэтому оно распространится по стеку.

Одновременный перехват нескольких типов

С помощью одной инструкции catch можно перехватывать несколько типов исключений одновременно.

Благодарю /u/Sheppard_Ra за предложение добавить этот раздел.

Вызов типизированных исключений

В PowerShell можно вызывать типизированные исключения. Вместо вызова throw со строкой:

используйте ускоритель исключений следующим образом:

Если вы не используете PowerShell 5.0 или более поздних версий, необходимо использовать устаревший подход с применением New-Object .

Как упоминалось в предыдущем разделе, используя типизированное исключение, вы (или другие пользователи) можете перехватывать исключения по типу.

Параметр -Exception командлета Write-Error

Эти типизированные исключения можно добавить в Write-Error и при этом перехватывать исключения с помощью catch по их типу. Используйте командлет Write-Error , как показано в следующих примерах.

Теперь исключение можно перехватить следующим образом.

Для начала я ищу в этом списке исключения, которые хорошо подходят для моей ситуации. Следует попытаться использовать исключения в базовом пространстве имен System .

Исключения как объекты

У него есть свойство FileName , которое предоставляет путь к этому файлу.

Повторный вызов исключения

Если блок catch используется только для вызова того же исключения с помощью throw , перехватывать его с помощью catch не стоит. Перехватывать с помощью catch следует только исключение, которое планируется обработать или использовать для выполнения какого-либо действия.

Интересно, что throw можно вызвать из catch , и тогда текущее исключение будет вызвано повторно.

Исключение создается повторно, чтобы сохранить исходные сведения о выполнении, например исходный скрипт и номер строки. Если на этом этапе вызывается новое исключение, оно скрывает первоначальное место его возникновения.

Повторный вызов нового исключения

Если вы перехватили исключение, но хотите вызвать другое, следует вложить исходное исключение в новое. Это позволяет объекту на более низком уровне стека получить к нему доступ как к $PSItem.Exception.InnerException .

$PSCmdlet.ThrowTerminatingError()

Декстер Дхами отметил, что для устранения этой проблемы я могу использовать ThrowTerminatingError() .

Вы заметили, что в качестве источника проблемы в нем указана функция Get-Resource ? Теперь пользователь узнает что-то полезное.

Так как значением $PSItem является ErrorRecord , можно таким же образом использовать ThrowTerminatingError для повторного вызова.

При этом источник ошибки изменится на командлет, а внутренние данные о функции будут скрыты от его пользователей.

Try как источник неустранимой ошибки

Кирк Манро указывает, что некоторые исключения являются неустранимыми ошибками только при выполнении внутри блока try/catch . В этом предоставленном им примере исключение во время выполнения вызывается вследствие деления на ноль.

Однако если поместить этот же код в try/catch , то происходит нечто иное.

Сам я не сталкивался с такой проблемой, но это крайний случай, о котором следует помнить.

Метод $PSCmdlet.ThrowTerminatingError() в try/catch

Одна из особенностей $PSCmdlet.ThrowTerminatingError() заключается в том, что в командлете этот метод вызывает неустранимую ошибку, но после выхода из него ошибка становится устранимой. Из-за этого тому, кто вызывает функцию, приходится решать, как следует обрабатывать такую ошибку. В этом случае можно превратить ее снова в неустранимую ошибку с помощью -ErrorAction Stop или воспользоваться вызовом из try<. >catch <. >.

Шаблоны общих функций

В одной из последних бесед с Кирком Манро мы говорили о том, что он помещает каждый блок begin , process и end во всех своих расширенных функциях в блок try<. >catch <. >. В этих универсальных блоках catch у него есть одна строка с методом $PSCmdlet.ThrowTerminatingError($PSItem) для обработки всех исключений, вызываемых его функциями.

Ключевое слово trap

Я уделил много внимания особенностям блока try/catch . Однако прежде чем закрывать эту тему, нужно упомянуть об одной устаревшей функции.

Ключевое слово trap помещается в скрипт или функцию для перехвата всех исключений, возникающих в соответствующей области. При возникновении исключения выполняется код в ловушке trap , а затем продолжается выполнение стандартного кода. Если происходит несколько исключений, ловушка вызывается снова и снова.

Я лично никогда не применял этот подход. Однако, судя по скриптам администраторов и контроллеров, которые заносят в журнал абсолютно все исключения, а затем продолжают выполнение, могу сказать, что в нем есть своя польза.

Заключительное слово

Добавление соответствующей обработки исключений в скрипты не только делает эти скрипты более стабильными, но и упрощает устранение таких исключений.

Я уделил так много внимания throw , так как это основное понятие, которым мы оперируем при обсуждении обработки исключений. В PowerShell также предоставляется командлет Write-Error , справляющийся со всеми ситуациями, в которых следовало бы использовать throw . Так что изложенное в этой статье вовсе не означает, что вам обязательно необходимо использовать throw .

Теперь, когда я подробно рассказал об обработке исключений, я собираюсь заняться вопросом использования Write-Error -Stop для создания ошибок в коде. Я также собираюсь воспользоваться советом Кирка и перейти на использование ThrowTerminatingError в качестве обработчика исключений для каждой функции.

Читайте также: