Если IF содержит более одной строки, сделайте это - PullRequest
2 голосов
/ 28 марта 2019

Попытка создать скрипт, который запрашивает дополнительную информацию (идентификатор группы), если есть группы SCOM с одинаковыми именами:

function myFunction {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string[]]$ObjectName
    )

    foreach ($o in $ObjectName) {
        $p = Get-SCOMGroup -DisplayName "$o" | select DisplayName
        <#
        if ($p contains more than one string) {
            "Request group Id"
        } else {
            "do this"
        }
        #>            
    }
}

Нужна помощь с функциональностью в блоке комментариев.

Ответы [ 2 ]

3 голосов
/ 28 марта 2019

Обернуть значение в подвыражение массива @() и подсчитать, сколько записей в нем:

if(@($p).Count -gt 1){"Request group Id"}
1 голос
/ 29 марта 2019

Примечание: этот ответ дополняет полезный ответ Матиаса Р. Йессена .

Подсчет количества объектов, возвращаемых командой:

  • Ответ Матиаса показывает надежное, совместимое с PowerShell v2 решение на основе оператора подвыражения массива , @().

    # @() ensures that the output of command ... is treated as an array,
    # even if the command emits only *one* object.
    # You can safely call .Count (or .Length) on the result to get the count.
    @(...).Count 
    
  • В PowerShell v3 или выше вы можете обрабатывать скаляры как коллекции , так что с использованием только (...).Count обычно достаточно . (Скаляр - это отдельный объект, а не набор объектов).

    # Even if command ... returns only *one* object, it is safe
    # to call .Count on the result in PSv3+
    (...).Count
    

Эти методы обычно , но не всегда взаимозаменяемы , как описано ниже.


  • Выберите @(...).Count, если:

    • Вы должны оставаться PSv2-совместимым
    • вы хотите посчитать вывод из нескольких команд (разделенных ; или символами новой строки)
    • для команд, выводящих целые коллекции как один объект (что встречается редко), вы хотите считать такие коллекции как 1 object .
    • в более общем случае, если вам нужно убедиться, что вывод команды возвращается как истинный массив , хотя обратите внимание, что он неизменно имеет тип [object[]]; если вам нужен определенный тип элемента , используйте приведение (например, [int[]]), но учтите, что тогда вам строго не нужен @(...); например.,
      [int[]] (...) подойдет - если вы не хотите запретить перечисление коллекций, выводимых как отдельные объекты.
  • Выберите (...).Count, если:

    • только одна команда должна быть подсчитана
    • для команд, которые выводят целые коллекции как один объект , вы хотите считать отдельные элементы таких коллекций; то есть (...) вызывает перечисление.
    • для подсчета элементов вывода команд , уже сохраненных в переменной - хотя, конечно, вы можете просто опустить (...) и использовать $var.Count

Предостережение : из-за давней ошибки (все еще присутствует в PowerShell Core 6.2.0), доступ к .Count в скаляре терпит неудачу, пока действует Set-StrictMode -Version 2 или выше - используйте @(...) в этом случае, но учтите, что вам, возможно, придется форсировать перечисление.

Чтобы продемонстрировать разницу в поведении относительно (редких) команд, которые выводят коллекции как отдельные объекты :

PS> @(Write-Output -NoEnumerate (1..10)).Count 
1  # Array-as-single-object was counted as *1* object

PS> (Write-Output -NoEnumerate (1..10)).Count 
10  # Elements were enumerated.

Особенности производительности :

  • Если вывод команды напрямую считается, (...) и @(...) работают примерно одинаково :

    $arr = 1..1e6  # Create an array of 1 million integers.
    
    { (Write-Output $arr).Count }, { @(Write-Output $arr).Count } | ForEach-Object {
      [pscustomobject] @{
        Command = "$_".Trim()
        Seconds = '{0:N3}' -f (Measure-Command $_).TotalSeconds
      }
    }
    
    • Пример вывода с одноядерной виртуальной машины Windows 10 (абсолютные значения времени не важны, только то, что цифры практически одинаковы):

      Command                    Seconds
      -------                    -------
      (Write-Output $arr).Count  0.352
      @(Write-Output $arr).Count 0.365
      
  • В отличие от этого, для больших коллекций , уже сохраненных в переменной , @(...) вносит существенные издержки , поскольку коллекция воссоздается как (новый) массив (как отметил, что вы можете просто $arr.Count):

    $arr = 1..1e6  # Create an array of 1 million integers.
    
    { ($arr).Count }, { @($arr).Count } | ForEach-Object {
      [pscustomobject] @{
        Command = "$_".Trim()
        Seconds = '{0:N3}' -f (Measure-Command $_).TotalSeconds
      }
    }
    
    • Пример вывода; обратите внимание, что решение @(...) примерно в 7 раз медленнее:

      Command       Seconds
      -------       -------
      ($arr).Count  0.009
      @($arr).Count 0.067
      

Соображения в отношении стиля кодирования :

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

Матиас рекомендует @(...).Count, заявив в комментарии:

Есть еще одна причина явно обернуть его в этом контексте - передача намерения , т. Е. "Мы не знаем, является ли $p скалярным или нет, следовательно, эта конструкция".

Мой голос за (...).Count:

Как только вы поймете, что PowerShell (v3 или выше) рассматривает скаляры как коллекции с числом 1 по требованию, вы можете использовать эти знания без необходимости отражать различие между скаляром и массивом в синтаксис :

  • При написании кода это означает , вам не нужно беспокоиться о том, может ли данная команда ситуационно возвращать скаляр вместоcollection (что является обычным в PowerShell, где при захвате вывода команды с помощью выходного объекта single этот объект фиксируется как есть, тогда как выходные объекты 2 или более приводят к массив ).

  • В качестве полезного побочного эффекта код становится более кратким (а иногда и быстрее).

Пример:

# Call Get-ChildItem twice, and, via Select-Object, limit the 
# number of output objects to 1 and 2, respectively.
1..2 | ForEach-Object {

  # * In the 1st iteration, $var becomes a *scalar* of type [System.IO.DirectoryInfo]
  # * In the 2nd iteration, $var becomes an *array* with 
  #   2 elements of type [System.IO.DirectoryInfo]
  $var = Get-ChildItem -Directory / | Select-Object -First $_

  # Treat $var as a collection, which in PSv3+ works even
  # if $var is a scalar:
  [pscustomobject] @{
    Count = $var.Count
    FirstElement = $var[0]
    DataType = $var.GetType().Name
  }

}

Выше приведено:

Count FirstElement  DataType
----- ------------  --------
    1 /Applications DirectoryInfo
    2 /Applications Object[]

То есть даже скалярный объект типа System.IO.DirectoryInfo сообщил о .Count разумно как 1 и разрешил доступ к «своему первому элементу»th [0].

Подробнее об объединенной обработке скаляров и коллекций см. этот ответ .

...