Почему значение аргумента по умолчанию для этой функции PowerShell изменяется в зависимости от использования.или &, чтобы вызвать команду внутри него? - PullRequest
0 голосов
/ 20 декабря 2018

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

Вот пример сценария с двумя идентичными функциями, где единственное отличие состоит в том, как они вызывают собственную команду (cmd.exe):

function Format-Type($value)
{
    if ($value -eq $null) { '(null)' } else { $value.GetType().FullName }
}

function Use-Dot
{
    param(
        [string] $Arg = [System.Management.Automation.Language.NullString]::Value
    )

    Write-Host ".: $(Format-Type $Arg)"

    . cmd.exe /c exit 0
}

function Use-Ampersand
{
    param(
        [string] $Arg = [System.Management.Automation.Language.NullString]::Value
    )

    Write-Host "&: $(Format-Type $Arg)"

    & cmd.exe /c exit 0
}

Use-Dot
Use-Ampersand

Вкл.PowerShell 5.1 Я получаю следующий вывод, который показывает, что значение аргумента отличается в двух случаях:

.: (null)
&: System.String

Звучит смешно, если такое поведение коррелируется таким образом, и поэтому я уверен, что должен бытьздесь упущено что-то очевидное (или, может быть, очень тонкое), но что это?

-

Вопрос Что такое сокращение . в конвейере PowerShell? говорит о разнице в области видимости между . и &, но здесь не упоминается, почему аргумент us может повлиять на аргумент по умолчанию, на который даже нет ссылки в вызове командые.В моем примере на вызывающего абонента, похоже, влияют до , команда даже вызывается.

Ответы [ 2 ]

0 голосов
/ 29 мая 2019

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

function Use-Dot
{
    param
    (
        [string]
        $Param1 = [System.Management.Automation.Language.NullString]::Value
    )

    $Param1.GetType()

    . {$null}
}

function Use-Invoke
{
    param
    (
        [string]
        $Param2 = [System.Management.Automation.Language.NullString]::Value
    )

    $Param2.GetType()

    & {$null}
}

Use-Dot
Use-Invoke

Это возвращает нулевую ошибку вызова для Use-Dot и возвращает тип строки для Use-Invoke:

PS C:\> C:\TestArgScope.ps1
You cannot call a method on a null-valued expression.
At c:\TestArgScope.ps1:10 char:5
+     $Param1.GetType()
+     ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull


IsPublic IsSerial Name                                     BaseType                                                        
-------- -------- ----                                     --------                                                        
True     True     String                                   System.Object   

Теперь рассмотрите возможность сделать это следующим образом:

function Use-Dot
{
    param
    (
        [string]
        $Param1
    )

    $Param1.GetType()

    . {$null}
}

function Use-Invoke
{
    param
    (
        [string]
        $Param2
    )

    $Param2.GetType()

    & {$null}
}

Use-Dot -Param1 [System.Management.Automation.Language.NullString]::Value
Use-Invoke -Param1 [System.Management.Automation.Language.NullString]::Value

Это выводит тип строки для обеих функций, что, я думаю, указывает на то, что проблема возникает во время привязки параметра, когда заданы значения параметров по умолчанию.Как указал Шелдон, точечный источник изменяет поведение преобразования типов для следующей за ним функции, но почему?У меня нет ответов на все вопросы, но есть пара комментариев:

  1. не должно быть причин для точечного источника вызова cmd.exe - точечный поиск имеет очень специфический вариант использования иэто не то же самое, что вызов команды.
  2. Я действительно не вижу причины использовать тип NullString вообще.Я знаю, что на самом деле это не ваш производственный сценарий, поэтому я не знаю, почему вы продемонстрировали это, но он действительно имеет определенное назначение для вызовов методов .NET.
  3. Dot-Sourcing внутрифункция, вероятно, является плохой идеей и может иметь некоторые неудачные результаты.
  4. Сравнение строк с $ null - плохая идея - зачем использовать это, когда вы можете просто сделать:

    if (!($Arg)) {<do something>}
    
0 голосов
/ 11 марта 2019

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

Во-первых, для ясности, я не верю, что вы должны рассматривать значение NullString как нулевое в базовом сравнении.Не уверен, зачем вам это нужно, так как обычно это то, что я ожидаю от разработки на c #.Вы должны иметь возможность просто использовать $null для большей части работы в PowerShell.



if($null -eq [System.Management.Automation.Language.NullString]::Value)
{
    write-host "`$null -eq [System.Management.Automation.Language.NullString]::Value"
}
else
{
    write-host "`$null -ne [System.Management.Automation.Language.NullString]::Value"
}

Во-вторых, проблема не обязательно связана с оператором вызова, то есть &.Я считаю, что вместо этого вы имеете дело с принудительным привязыванием параметров.Строгая типизация данных, безусловно, является слабой областью для PowerShell, так как даже явно объявленный [int]$val может в конечном итоге установить PowerShell для строкового типа автоматически в следующей строке при записи Write-Host $Val.

Чтобы определить основнойВ своем поведении я использовал функцию Trace-Command ( Trace Command ).

Я изменил Use-Dot, чтобы просто вызывать функцию, поскольку для вывода строки не требовался хост записи.


function Use-Ampersand
{
    param(
        [string]$NullString = [System.Management.Automation.Language.NullString]::Value
    )
    Format-Type $NullString
    &cmd.exe /c exit 0
}

Формат-Type I был изменен, чтобы также использовать то, что считаетсялучшая практика $null слева, опять же из-за вывода типа.

function Format-Type($v= [System.Management.Automation.Language.NullString]::Value)
{

    if ($null  -eq $v)
    {       
     '(null)'
    }
    else {
        $v.GetType().FullName
     }
}

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

Trace-Command -Name TypeConversion -Expression { Format-Type $NullString} -PSHost
Trace-Command -Name TypeConversion -Expression { Format-Type ([System.Management.Automation.Language.NullString]$NullString) } -PSHost

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

Trace-Command -Name TypeConversion  -Expression { Use-Dot} -PSHost
Trace-Command -Name TypeConversion  -Expression { Use-Ampersand} -PSHost
# USE DOT
DEBUG: TypeConversion Information: 0 :  Converting "" to "System.String".
DEBUG: TypeConversion Information: 0 :      Converting object to string.
DEBUG: TypeConversion Information: 0 :  Converting "" to "System.Object". <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 :  Converting ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL" to "System.String".
DEBUG: TypeConversion Information: 0 :      Result type is assignable from value to convert's type

OUTPUT: (null)

# Use-Ampersand
DEBUG: TypeConversion Information: 0 : Converting "" to "System.String".
DEBUG: TypeConversion Information: 0 :     Converting object to string.
DEBUG: TypeConversion Information: 0 : Converting "" to "System.String". <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 :     Converting null to "".        <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 : Converting ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL" to "System.String".
DEBUG: TypeConversion Information: 0 :     Result type is assignable from value to convert's type

OUTPUT: System.String

Заметная разница в Use-Ampersand, это показывает утверждение Converting null to "" против Converting "" to "System.Object".В PowerShell $null <> [string]''.Сравнение пустой строки пройдет проверку на ноль, что приведет к успешному выводу GetType().

Несколько мыслей о подходе с PowerShell

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

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

Да, вы можете использовать расширения типа.Да, вы можете использовать типы данных .NET, такие как $List = [System.Collections.Generic.List[string]]::new(), и могут применяться некоторые типизированные правила .NET.Однако PowerShell не предназначен для строго типизированного языка, такого как C #.Попытка приблизиться к нему таким образом приведет к множеству трудностей.Несмотря на то, что я большой поклонник PowerShell, я научился понимать, что его гибкость следует ценить, а ограничения - уважать.

Если бы у меня действительно были проблемы, которые требовали столь сильного сопоставления [System.Management.Automation.Language.NullString]::Value, я бы рассмотрел свой подход.

Тем не менее, это было сложное расследование, в котором я должен был принять участие, предоставив при этом свои 10 центов.

Другие ресурсы

После публикации моего ответа я нашел еще один ответ , который показался мне уместным, а также поддерживает упоминание о том, что [NullString] не используется как обычный способ использования.в PowerShell не совсем то, для чего он был разработан.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...