Расширяем возможности однострочных команд PowerShell

В статьях «Конвейер и ForEach» и «Изучаем однострочные команды ForEach» было показано, как простые однострочные команды можно записывать по-другому с помощью команды ForEach и переменной $_, хранящей содержимое конвейера. Предположим, что в организации bigfirm.com существует подразделение (OU) под названием Machinists. Требуется заменить описание каждой учетной записи пользователя, то есть значение атрибута description, на текст Machinist. Это можно сделать с помощью простой однострочной команды, подобной той, что рассматривалась в статье «Поиск пользователей с помощью Get-ADUser»:

get-aduser -filter * -searchbase «ou=machinists,dc=bigfirm,dc=com» | set-aduser -description «Machinist»

Эту однострочную команду можно переписать с использованием ForEach и $_:

get-aduser -filter * -searchbase «ou=machinists,dc=bigfirm,dc=com» | foreach {set-aduser $_ -description «Machinist»}

Второй вариант делает то же, что и первый, но при этом он более сложен. В чем же его преимущества? Ответ прост: в гибкости. Предположим, что описание пользователя требуется изменить не на Machinist, а на нечто более сложное. Например, нужно извлечь имя каждого пользователя и задать его описание в форме [Имя] the Machinist (например, Dan the Machinist). С этой задачей простые однострочные команды, которые мы изучали до сих пор, не справляются, так как просто не предусматривают такой возможности.

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

1. Имя пользователя Active Directory (AD) несет атрибут givenname.

2. Как и всякий атрибут, его можно извлечь с помощью команды Get-ADUser с добавлением. givenname, как в примере извлечения имени пользователя jdorn:

$someuser = (get-aduser jdorn)
$someuser.givenname

3. Встроенная переменная $_ хранит содержимое конвейера, каковым после get-aduser является искомый пользователь, поэтому $_.givenname даст имя этого пользователя.

4. Мы уже знаем, что можно использовать знак «плюс» (+) для склеивания (конкатенации) двух текстовых строк, поэтому создать описание Dan the Machinist при условии, что содержимым конвейера является учетная запись пользователя по имени Dan, должна следующая запись:

$_.givenname + «the Machinist»

5. Получается, что должно сработать следующее:

get-aduser -filter * -searchbase «ou=machinists,dc=bigfirm,dc=com» | set-aduser -description ($_.givenname + «the Machinist»)

Однако, хотя мои рассуждения логичны, такой вариант не работает. Почему?

Однострочные команды PowerShell для AD, которые мы изучали до сих пор, очень полезны, поскольку многие из них вмещают действия, для программирования которых обычно требуется 50 строк сценария на языке VBScript. PowerShell маскирует сложность принятия решений и циклической обработки. Однако с их помощью можно делать не так уж много. Основное правило состоит в том, что всякое применение переменной $_ справа от символа конвейера (как в нашем случае) влечет за собой необходимость задействовать ForEach и блок сценария. Я упрощаю, но для команд, предназначенных для работы с AD, это справедливо.

Итак, чтобы заставить нашу однострочную команду работать, достаточно ее переписать в несколько более сложной форме с использованием ForEach и $_:

get-aduser -filter * -searchbase «ou=machinists,dc=bigfirm,dc=com» | foreach {set-aduser $_ -description ($_.givenname + «the Machinist»)}

Применение ForEach не слишком усложнит вам жизнь. Вспомним структуру простой однострочной команды: фильтр, за которым следует действие, например:

get-aduser | unlock-adaccount

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

  1. Фильтр (часть слева от символа конвейера) оставляем без изменения.
  2. Символ конвейера также оставляем на месте.
  3. Пишем foreach { перед блоком сценария.
  4. Выполнение «деятельной» части однострочной команды справа от символа конвейера требует знания имени учетной записи пользователя, к которой применяется указанное действие. В конструкции ForEach команда не видит автоматически объект, выходящий с конвейера, как в простой однострочной команде. Необходимо указать имя учетной записи пользователя: $_. Таким образом, вместо unlock-adaccount пишем unlock-adaccount $_.
  5. При необходимости дописываем команду – например, указываем извлекаемое свойство выходящей с конвейера учетной записи ($_.свойство), каковым в нашем случае является givenname. Помните, что Get-ADUser по умолчанию возвращает лишь около 10 из многих десятков свойств пользователя AD. Если требуется вывести все свойства, добавьте properties *.
  6. Закрываем блок сценария символом }.

В следующий раз мы рассмотрим другие примеры.