Подводные камни Powershell - PullRequest
       29

Подводные камни Powershell

24 голосов
/ 29 апреля 2009

В какие ловушки Powershell вы попадаете? :-)

Шахты:

# -----------------------------------
function foo()
{
    @("text")
}

# Expected 1, actually 4.
(foo).length

# -----------------------------------
if(@($null, $null))
{
    Write-Host "Expected to be here, and I am here."
}

if(@($null))
{
    Write-Host "Expected to be here, BUT NEVER EVER."
}

# -----------------------------------

function foo($a)
{
    # I thought this is right.
    #if($a -eq $null)
    #{
    #    throw "You can't pass $null as argument."
    #}

    # But actually it should be:
    if($null -eq $a)
    {
        throw "You can't pass $null as argument."
    }
}

foo @($null, $null)

# -----------------------------------

# There is try/catch, but no callstack reported.
function foo() 
{
   bar
}

function bar() 
{
  throw "test"
}

# Expected:
#  At bar() line:XX
#  At foo() line:XX
#  
# Actually some like this:
#  At bar() line:XX
foo

Хотелось бы знать ваши, чтобы выгуливать их: -)

Ответы [ 20 ]

2 голосов
/ 29 апреля 2009

alex2k8, я думаю, что ваш пример хорош для разговора:

# -----------------------------------
function foo($a){
    # I thought this is right.
    #if($a -eq $null)
    #{
    #    throw "You can't pass $null as argument."
    #}
    # But actually it should be:
    if($null -eq $a)
    {
        throw "You can't pass $null as argument." 
    }
}
foo @($null, $null)

PowerShell может использовать некоторые из компараторов для таких массивов, как этот:

$array -eq $value
## Returns all values in $array that equal $value

Имея это в виду, исходный пример возвращает два элемента (два значения $ null в массиве), которые оцениваются в $ true, поскольку в итоге получается коллекция из более чем одного элемента. Изменение порядка аргументов останавливает сравнение массивов.

Эта функциональность очень полезна в определенных ситуациях, но вам нужно знать об этом (как при обработке массива в PowerShell).

1 голос
/ 13 января 2011

Логические и побитовые операторы не следуют стандартным правилам приоритета. Оператор -и должен иметь более высокий приоритет, чем -или, но он оценивается строго слева направо.

Например, сравните логические операторы между PowerShell и Python (или практически любым другим современным языком):

# PowerShell
PS> $true -or $false -and $false
False

# Python
>>> True or False and False
True

... и побитовые операторы:

# PowerShell
PS> 1 -bor 0 -band 0
0

# Python
>>> 1 | 0 & 0
1
1 голос
/ 23 октября 2009

Еще один:

$x = 2
$y = 3
$a,$b = $x,$y*5 

из-за приоритета операторов в $ b нет 25; команда такая же как ($ x, $ y) * 5 правильная версия

$a,$b = $x,($y*5)
0 голосов
/ 25 декабря 2013

Мои данные связаны с копированием файлов ...

Квадратные скобки в именах файлов
Однажды мне пришлось переместить очень большую / сложную структуру папок, используя Move-Item -Path C:\Source -Destination C:\Dest. В конце процесса в исходном каталоге было еще несколько файлов. Я заметил, что у каждого оставшегося файла в имени были квадратные скобки.

Проблема заключалась в том, что параметр -Path обрабатывает квадратные скобки как символы подстановки.
НАПРИМЕР. Если вы хотите скопировать Log001 в Log200, вы можете использовать квадратные скобки следующим образом: Move-Item -Path C:\Source\Log[001-200].log.

В моем случае, чтобы избежать интерпретации квадратных скобок как символов подстановки, я должен был использовать параметр -LiteralPath.

ErrorActionPreference
Переменная $ErrorActionPreference игнорируется при использовании Move-Item и Copy-Item с параметром -Verbose.

0 голосов
/ 29 ноября 2012

Не забывайте явно вводить объекты pscustom из импортированных таблиц данных в виде чисел, чтобы их можно было правильно отсортировать:

$CVAP_WA=foreach ($i in $C){[PSCustomObject]@{ `
                County=$i.county; `
                TotalVote=[INT]$i.TotalBallots; `
                RegVoters=[INT]$i.regvoters; `
                Turnout_PCT=($i.TotalBallots/$i.regvoters)*100; `
                CVAP=[INT]($B | ? {$_.GeoName -match $i.county}).CVAP_EST }}

PS C: \ Politics> $ CVAP_WA | sort -desc TotalVote | ft -auto -wrap

County       TotalVote RegVoters Turnout_PCT    CVAP CVAP_TV_PCT CVAP_RV_PCT
------       --------- --------- -----------    ---- ----------- -----------
King            973088   1170638      83.189 1299290      74.893      90.099
Pierce          349377    442985       78.86  554975      62.959      79.837
Snohomish       334354    415504      80.461  478440      69.832       86.81
Spokane         227007    282442      80.346  342060      66.398      82.555
Clark           193102    243155      79.453  284190      67.911       85.52
0 голосов
/ 20 ноября 2011

Забыв, что $ _ перезаписывается в блоках, я пару раз почесал голову в замешательстве, и аналогично для нескольких регулярных совпадений и массива $ совпадений. >. <</p>

0 голосов
/ 08 мая 2009

Еще один вопрос, с которым я недавно столкнулся: параметры [string], которые принимают входные данные конвейера, на практике не являются строго типизированными. Вы можете передать что-нибудь вообще, и PS будет принудительно вызывать это через ToString ().

function Foo 
{
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [string] $param
    )

    process { $param }
}

get-process svchost | Foo

К сожалению, нет способа отключить это. Лучший обходной путь, о котором я мог подумать:

function Bar
{
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [object] $param
    )

    process 
    { 
        if ($param -isnot [string]) {
            throw "Pass a string you fool!"
        }
        # rest of function goes here
    }
}

edit - лучший обходной путь, который я начал использовать ...

Добавьте это к своему пользовательскому типу XML -

<?xml version="1.0" encoding="utf-8" ?>
<Types>
  <Type>
    <Name>System.String</Name>
    <Members>
      <ScriptProperty>
        <Name>StringValue</Name>
        <GetScriptBlock>
          $this
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>

Затем напишите такие функции:

function Bar
{
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
        [Alias("StringValue")]
        [string] $param
    )

    process 
    { 
        # rest of function goes here
    }
}
0 голосов
/ 29 апреля 2009

Это работает. Но почти наверняка не так, как вы думаете, это работает.

PS> $a = 42;
PS> [scriptblock]$b = { $a }
PS> & $b
42
0 голосов
/ 30 апреля 2009
# $x is not defined
[70]: $x -lt 0
True
[71]: [int]$x -eq 0
True

Итак, что такое $ x ..?

0 голосов
/ 29 апреля 2009

Этот ранее меня уже сработал, используя $ o.SomeProperty, где он должен быть $ ($ o.SomeProperty).

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