2016-02-27 2 views
0

У меня есть следующий код ...итерации пропуска через какую-то сверхъестественную причиной

Function PrintArrayAsGrid 
{ 
    param([string[]]$Array,[ValidateRange(1,24)][int]$ColumnCount) 

    $GridSplat = @{ 
     InputObject = $Array|ForEach-Object { 
      New-Object psobject -Property @{'Value' = $_} 
     } 
     Property = 'Value' 
    } 

    if(-not $PSBoundParameters.ContainsKey('ColumnCount')) 
    { 
     $GridSplat['AutoSize'] = $true 
    } 
    else 
    { 
     $GridSplat['Column'] = $ColumnCount 
    } 

    Format-Wide @GridSplat 
} 

Function UserInputAdSearchPropertyName 
{ 
    $userInputNotYetValidated = $true 
    $userInput = Read-Host " 
    Enter AD property name you wish to search with (e.g.: employeeNumber)" 

    $userInput = $userInput.Trim() 
    Write-Host "you input $userInput" 
    Write-Host "" 

    [String[]]$validAdProperties = @('SamAccountName', 'msRTCSIP-UserEnabled', 'msRTCSIP-OptionFlags', 'msRTCSIP-PrimaryUserAddress', 'msRTCSIP-PrimaryHomeServer', 
    'mail', 'msExchMasterAccountSid', 'homeMDB', 'proxyaddresses', 'legacyExchangeDN', 
    'lastLogonTimestamp', 'logonCount', 'lastLogoff', 'lastLogon', 'pwdLastSet', 'userAccountControl', 'whenCreated', 'whenChanged', 'accountExpires', 
    'sn', 'givenName', 'displayName', 'distinguishedName', 'initials', 'l', 'st', 'street', 'title', 'description', 'postalCode', 'physicalDeliveryOfficeName', 'telephoneNumber', 'facsimileTelephoneNumber', 'info', 'memberOf', 'co', 'department', 'company', 'streetAddress', 'employeeNumber', 'employeeType', 'objectGUID', 'employeeID', 'homeDirectory', 'homeDrive', 'scriptPath', 'objectSid', 'userPrincipalName', 'url', 'msDS-SourceObjectDN', 'manager', 'extensionattribute8') 

    while ($userInputNotYetValidated) 
    { 
     If ($validAdProperties -notcontains $userInput) 
     { 
      Write-Error "Invalid AD Property Name: $userInput" 

      PrintArrayAsGrid $validAdProperties 4 

      $userInput = Read-Host " Enter one property name from list above to search with" 
      Write-Host "you input $userInput" 
      Write-Host "" 
     } Else { 
      $userInputNotYetValidated = $false 
     } 
    } 
    Write-Output $userInput 
} 

# Ask user to enter property name in AD to search with 
$searchAdPropertyName = UserInputAdSearchPropertyName 

Выход:

 Enter AD property name you wish to search with (e.g.: employeeNumber): asdf 
you input asdf 

UserInputAdSearchPropertyName : Invalid AD Property Name: asdf 
At C:\Scripts\Tests\temp2.ps1:59 char:26 
+  $searchAdPropertyName = UserInputAdSearchPropertyName 
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,UserInputAdSearchPropertyName 

     Enter one property name from list above to search with: 

вопрос здесь это не печатает список свойств и пропуская над итерация, где она вызывает функцию с PrintArrayAsGrid $validAdProperties 4

Вот что произойдет, если я добавлю следующие строки ...

  Write-Host 'Found' 
      PrintArrayAsGrid $validAdProperties 4 
      Write-Host 'Found' 

выход:

  Enter AD property name you wish to search with (e.g.: employeeNumber): asdf 
you input asdf 

UserInputAdSearchPropertyName : Invalid AD Property Name: asdf 
At C:\Scripts\Tests\temp2.ps1:60 char:26 
+  $searchAdPropertyName = UserInputAdSearchPropertyName 
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,UserInputAdSearchPropertyName 

Found 
Found 
     Enter one property name from list above to search with: 

И теперь, я на самом деле получить таблицу распечатаны, если добавить в выходе следующим образом ...

  Write-Host 'Found' 
      PrintArrayAsGrid $validAdProperties 4 
      Write-Host 'Found' 
      Exit 

Выход:

PS C:\Tests> .\test1.ps1 

       Enter AD property name you wish to search with (e.g.: employeeNumber): asdf 
you input asdf 

UserInputAdSearchPropertyName : Invalid AD Property Name: asdf 
At C:\Tests\test1.ps1:61 char:26 
+  $searchAdPropertyName = UserInputAdSearchPropertyName 
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,UserInputAdSearchPropertyName 

Found 
Found 


SamAccountName    msRTCSIP-UserEnabled   msRTCSIP-OptionFlags   msRTCSIP-PrimaryUserAddress 
msRTCSIP-PrimaryHomeServer mail       msExchMasterAccountSid  homeMDB 
proxyaddresses    legacyExchangeDN    lastLogonTimestamp   logonCount 
lastLogoff     lastLogon      pwdLastSet     userAccountControl 
whenCreated     whenChanged     accountExpires    sn 
givenName      displayName     distinguishedName    initials 
l        st       street      title 
description     postalCode     physicalDeliveryOfficeName telephoneNumber 
facsimileTelephoneNumber  info       memberOf      co 
department     company      streetAddress     employeeNumber 
employeeType     objectGUID     employeeID     homeDirectory 
homeDrive      scriptPath     objectSid      userPrincipalName 
url       msDS-SourceObjectDN   manager      extensionattribute8 


PS C:\Tests> 

Желаемый результат:

PS C:\Tests> .\test1.ps1 

       Enter AD property name you wish to search with (e.g.: employeeNumber): asdf 
you input asdf 

UserInputAdSearchPropertyName : Invalid AD Property Name: asdf 
At C:\Tests\test1.ps1:61 char:26 
+  $searchAdPropertyName = UserInputAdSearchPropertyName 
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,UserInputAdSearchPropertyName 


SamAccountName    msRTCSIP-UserEnabled   msRTCSIP-OptionFlags   msRTCSIP-PrimaryUserAddress 
msRTCSIP-PrimaryHomeServer mail       msExchMasterAccountSid  homeMDB 
proxyaddresses    legacyExchangeDN    lastLogonTimestamp   logonCount 
lastLogoff     lastLogon      pwdLastSet     userAccountControl 
whenCreated     whenChanged     accountExpires    sn 
givenName      displayName     distinguishedName    initials 
l        st       street      title 
description     postalCode     physicalDeliveryOfficeName telephoneNumber 
facsimileTelephoneNumber  info       memberOf      co 
department     company      streetAddress     employeeNumber 
employeeType     objectGUID     employeeID     homeDirectory 
homeDrive      scriptPath     objectSid      userPrincipalName 
url       msDS-SourceObjectDN   manager      extensionattribute8 


     Enter one property name from list above to search with: 

Кто-нибудь знает, почему это происходит?

+0

Вы уверены, что ваш 'Format-Wide @ GridSplat' линия производит любой вывод? Почему вы думаете, что он что-то пропускает? – n0rd

+0

Также вы смешиваете 'Write-Output' и' Write-Host' в своем коде, это намеренно? – n0rd

+0

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

ответ

1

В основном, как было упомянуто Чарли Джойнт, Format-Wide записывается в отдельный поток, который не сбрасывается до тех пор, пока скрипт не завершит выход, или пока не вызывается return или Write-Output.

Поскольку родительская функция намеренно использует Write-Output, Format-Wide застрял бы в линии потока и получил бы ее значения, присвоенные любым значением переменной, передаваемым через родительскую функцию (также).

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

Чтобы сделать все это, я просто изменить линию от этого:

Format-Wide @GridSplat 

Для этого:

$table = Format-Wide @GridSplat | Out-String 
Write-Host $table 
+0

Вы можете сделать это в одном выражении: 'Format-Wide @GridSplat | Out-Host' –

+0

Полезно знать, спасибо :) –

0

Такое происходит, когда у вас есть текст, явно написанный обратно на хост (Write-Host) и возвращаемый текст, как если бы результат от функции.

Когда вы вызываете команду или выражение, которое возвращает текст (например), просто вызывая эту функцию напрямую (в отличие от назначения вывода переменной или трубопровода для последующей команды), то это отправляется на «выход», поток, как и то, что вы получите при использовании Write-Output или return. В вашем случае отформатированный список атрибутов включается в вывод функции, а не записывается точно, когда вы хотите, чтобы он отображался.

Если вы проверили значение $searchAdPropertyName, вы увидите, что он включает в себя как отформатированный список атрибутов , так и имя одного атрибута, введенное в командной строке.

Поскольку вывод PrintArrayAsGrid - это то, что вы получаете из командлета Format-Wide, это не обычная строка, которую вы можете просто написать, как и другие, что делает это немного сложнее. Я не буду пытаться исправить это в этом ответе, поскольку я собираюсь ответить на вопрос «почему это происходит»!

BTW, вы можете использовать Write-Warning вместо Write-Error, когда кто-то вводит неправильное имя атрибута; это будет подавлять некоторые беспорядки, которые вы получаете с полной ошибкой.

+0

Ничего себе, ты прав. Все, что я сделал, это удалить строку «Write-Output», и это сетка напечатана в то время как внутри цикла while. Weird. Я привык к языкам, которые возвращают только то, что вы говорите, чтобы вернуться. Мне нужно подумать об этом ... –

+0

Прочтите этот [блог ScriptingGuy] (https://blogs.technet.microsoft.com/heyscriptingguy/2015/07/04/weekend-scripter-welcome -в-заместитель-информационный PowerShell-поток /). В нем рассказывается о потоках вывода и о том, как отличается «Write-Host». –

+0

Итак, я просмотрел статью, но, по-видимому, информация о записи не была добавлена ​​до версии v5, и у меня есть v3. Вот почему я получаю непризнанную ошибку cmdlet. После нескольких часов работы с форматом Wide, назначая его объекту и играя с типами объектов, я смог выяснить, как заставить его распечатать таблицу по команде (не присваивая ее выходному сигналу из родительская функция). Я скоро напишу ответ. –

0

Альтернативный подход к тому, что вы пытаетесь сделать, - это открыть диалоговое окно для выбора пользователем правильного атрибута. Вы можете сделать это, передав массив допустимых атрибутов с помощью командлета Out-GridView и присвоив результат переменной $userInput.Отрывок ниже:

... 
Write-Warning "Invalid AD Property Name: $userInput" 

$userInput = $validAdProperties | Out-GridView -Passthru 
... 

Если вы не возражаете список атрибутов, появляющихся в другом окне, то это по крайней мере гарантирует, что при выборе атрибута это, безусловно, будет что-то из вашего списка верного!

+0

Спасибо за предложение. Я действительно не хочу, чтобы сценарий открывал новые окна. Если есть способ сделать это просто распечатать, когда он будет вызван, было бы здорово. –

0

Другим подходом было бы определить переменную $userInput в качестве параметра и использовать атрибут ValidateSet, чтобы гарантировать, что пользователь может вводить только собственные имена свойств AD.

function UserInputAdSearchPropertyName 
{ 
    param(
     [ValidateSet('SamAccountName', 'msRTCSIP-UserEnabled', 'msRTCSIP-OptionFlags', 'msRTCSIP-PrimaryUserAddress', 'msRTCSIP-PrimaryHomeServer', 
      'mail', 'msExchMasterAccountSid', 'homeMDB', 'proxyaddresses', 'legacyExchangeDN', 
      'lastLogonTimestamp', 'logonCount', 'lastLogoff', 'lastLogon', 'pwdLastSet', 'userAccountControl', 'whenCreated', 'whenChanged', 'accountExpires', 
      'sn', 'givenName', 'displayName', 'distinguishedName', 'initials', 'l', 'st', 'street', 'title', 'description', 'postalCode', 'physicalDeliveryOfficeName', 'telephoneNumber', 'facsimileTelephoneNumber', 'info', 'memberOf', 'co', 'department', 'company', 'streetAddress', 'employeeNumber', 'employeeType', 'objectGUID', 'employeeID', 'homeDirectory', 'homeDrive', 'scriptPath', 'objectSid', 'userPrincipalName', 'url', 'msDS-SourceObjectDN', 'manager', 'extensionattribute8' 
      )] 
     [string]$userInput 
    ) 

    # rest of function here 
} 

Это позволит пользователю табуляцией полный допустимых входных строк:

tab-complete validateset

+0

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

+0

Вы можете использовать это и предоставить своим пользователям модуль 'PSReadLine' или Win10 (где он предварительно установлен). Затем они могут написать 'userinputtest -userinput' и нажать ctrl + space, чтобы отобразить доступные значения. –

Смежные вопросы