Недавно ко мне обратились с вопросом о странном поведении достаточно простой функции. Она создаёт локального пользователя с заданным именем и паролем, и должна возвращать в качестве результата объект с двумя свойствами: UserName и Password (Ну на самом деле функция была чуть сложнее, но нам интересна только эта часть ).
Вот её код:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function CreateUser ($Name, $Password) { trap {"Error creating user!"; break} #Перехват ошибок $computer = [ADSI]"WinNT://$env:computername" #Подключаемся к локальному компьютеру по ADSI $user = $computer.Create("user", $Name) #Создаём объект типа user $user.SetPassword($Password) #Устанавливаем пароль $user.SetInfo() #Записываем изменения $UP = New-Object PSObject #Создаём пустой объект $UP | Add-Member NoteProperty Username $Name #Добавляем свойства $UP | Add-Member NoteProperty Password $Password #И ещё $UP #Возвращаем объект } |
Теперь попробуем создать пользователя с помощью этой функции:
PS C:\Windows\system32> $Result = createUser -name Tester -pass P@ssw0rd
PS C:\Windows\system32> $Result
Username Password
-------- --------
Tester P@ssw0rd
PS C:\Windows\system32> $Result.UserName
PS C:\Windows\system32> $Result.Password
PS C:\Windows\system32>
Странно? Если просто вызываем переменную, то видно 2 свойства: UserName, Password, и их значения. Однако при попытке просто получить значения этих свойств мы получаем… ничего. В чём же дело?
PS C:\Windows\system32> $Result.gettype().name
Object[]
PS C:\Windows\system32> $Result.count
3
Квадратные скобки в конце типа объекта, говорят о том что это не просто объект, а массив. Причем как говорит его свойство .Count – из трех элементов. Зная это можно обратиться к свойствам:
PS C:\Windows\system32> $Result[2].Username
Tester
PS C:\Windows\system32> $Result[2].Password
P@ssw0rd
Нужный нам элемент будет лишь третьим по счёту (массивы индексируются с 0). Что касается первых двух элементов:
PS C:\Windows\system32> $Result[0].gettype().name
You cannot call a method on a null-valued expression.
At line:1 char:19
+ $Result[0].gettype <<< $Result[0] -eq $null
True
Их просто не существует, точнее они равны $null, пустоте. А взялись они благодаря вызовам методов ADSI SetPassword() и SetInfo(). Уж не знаю зачем, но видимо авторы данных методов посчитали это прикольным А в PowerShell есть одна тонкость: все данные которые не присваиваются переменной, не передаются по конвейеру, и не обрабатываются другим способом – отправляются на вывод. То есть функция function test {Get-process powershell; "Test"; del c:\test.txt; return 2+1; 2}
вернёт вам массив состоящий из объекта процесса, строки Test и числа 3. Двойка в конце не вернётся лишь потому что return предотвращает дальнейший вывод данных (возвращает не единственный результат, а лишь последний из ряда).
Короче говоря в нашей ситуации $null’ы возвращённые методами, присоеденились к результату возвращаемому функцией, и всё испортили Чтобы этого не произошло, можно избавиться от этих $null’ов, например… отправив их в Null
$user.SetPassword($Password) | Out-Null
$user.SetInfo() | Out-Null