programing

파워셸 함정

closeapi 2023. 8. 29. 20:39
반응형

파워셸 함정

어떤 파워셸 함정에 빠졌나요? :-)

내 항목:

# -----------------------------------
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

그들을 산책시키기 위해 당신의 것을 알고 싶습니다 :-)

제가 개인적으로 가장 좋아하는 것은

function foo() {
  param ( $param1, $param2 = $(throw "Need a second parameter"))
  ...
}

foo (1,2)

powershell에 익숙하지 않은 사용자의 경우 2개의 매개 변수를 전달하는 대신 실제로 배열을 생성하고 하나의 매개 변수를 전달하기 때문에 라인이 느려집니다.당신은 그것을 다음과 같이 불러야 합니다.

foo 1 2

또 하나의 재미있는 것.기본적으로 식을 처리하지 않으면 파이프라인에 씁니다.특정 함수가 값을 반환한다는 것을 깨닫지 못할 때 정말 짜증이 납니다.

function example() {
  param ( $p1 ) {
  if ( $p1 ) {
    42
  }
  "done"
}

PS> example $true 
42
"done"
$files = Get-ChildItem . -inc *.extdoesntexist
foreach ($file in $files) {
    "$($file.Fullname.substring(2))"
}

실패 대상:

You cannot call a method on a null-valued expression.
At line:3 char:25
+ $file.Fullname.substring <<<< (2)

다음과 같이 수정합니다.

$files = @(Get-ChildItem . -inc *.extdoesntexist)
foreach ($file in $files) {
    "$($file.Fullname.substring(2))"
}

결론은 스칼라 값이 $null인 경우에도 각 문에 대한 루프가 스칼라 값에 적용된다는 것입니다.첫 번째 예제의 Get-ChildItem이 아무것도 반환하지 않으면 $files에 $null이 할당됩니다.명령에 의해 항목 배열이 반환될 것으로 예상되지만 항목이 1개만 반환되거나 0개만 반환될 가능성이 있는 경우에는 명령 주위에 @()를 입력합니다.그러면 항상 0, 1 또는 N개의 항목으로 구성된 배열을 얻을 수 있습니다.: 항목이이배퍼열경우팅인미참고▁putting경▁note우.@()효과가 없습니다. 여전히 동일한 배열입니다(즉, 추가 배열 래퍼가 없습니다).

# The pipeline doesn't enumerate hashtables.
$ht = @{"foo" = 1; "bar" = 2}
$ht | measure

# Workaround: call GetEnumerator
$ht.GetEnumerator() | measure

파워셸 고차스

StackOverflow에 반복적으로 나타나는 함정이 몇 가지 있습니다.이러한 PowerShell gotchas에 익숙하지 않은 경우 새로운 질문을 하기 에 몇 가지 조사를 수행하는 것이 좋습니다.PowerShell 질문에 답하기 에 PowerShell gotchas에서 질문자에게 올바른 것을 가르쳐야 합니다.

TLDR: PowerShell에서:

  1. 는 다음과 같습니다.-eq
    (스택 오버플로 예: 조건이 작동하지 않는 경우 Powershell 단순 구문)
  2. 괄호 및 쉼표는 인수와 함께 사용되지 않습니다.
    (스택 오버플로 예:PowerShell에서 여러 매개 변수를 함수로 전달하려면 어떻게 해야 합니까?)
  3. 의 첫 .
    (스택 오버플로 예:일부 속성표시되지 않음)

  4. (스택 오버플로 예: 배열 항목 대신 완전 배열 개체를 한 번에 하나씩 파이프로 연결하시겠습니까?)

    (스택 오버플로 예: Powershell ArrayList는 단일 배열 항목을 문자열로 되돌립니다.)
    어레이
    (스택 오버플로 예제: 함수에서 다차원 배열 반환)
    모음집
    (스택 오버플로 예:PowerShell에서 어레이를 자동으로 평탄화하는 이유는 무엇입니까?)
  5. $Null 연산자의 .
    (스택 오버플로 예:$null이 동등성 비교의 왼쪽에 있어야 합니다.)
  6. 시킵니다.
    (스택 오버플로 예:16MB CSV를 변수로 가져오면 600MB 이상의 메모리 사용량이 생성됨)
  7. 연산자+= 수 있습니다.
    스택 오버플로 예:PowerShell 스크립트의 효율성 향상
  8. Get-Content은 별도의 linescmdlet을합니다.
    스택 오버플로 예:구성 블록과 일치하는 다중 행 정규식

예제 및 설명

일부 gotchas는 정말로 직관에 반하는 것처럼 느껴질 수 있지만 파이프라인, /인수 모드 및 유형 캐스팅과 함께 일부 매우 훌륭한 PowerShell 기능으로 설명될 수 있습니다.

는 다음과 같습니다.-eq

Microsoft 스크립트 언어 VBScript 및 일부 다른 프로그래밍 언어와 달리 비교 동등 연산자는 할당 연산자()=와 다르며 다음과 같습니다.

참고: 필요한 경우 변수에 값을 할당할 수 있습니다.

$a = $b = 3   # The value 3 is assigned to both variables $a and $b.

이는 다음 문장이 예상외로 사실이거나 거짓일 수 있음을 의미합니다.

If ($a = $b) {
    # (assigns $b to $a and) returns a truthy if $b is e.g. 3
} else {
    # (assigns $b to $a and) returns a falsy if $b is e.g. 0
}

괄호 및 쉼표는 인수와 함께 사용되지 않습니다.

다른 많은 프로그래밍 언어와 원시 PowerShell 함수가 정의된 방식과는 달리 함수를 호출할 때 관련 인수에 괄호나 쉼표가 필요하지 않습니다.공백을 사용하여 매개 변수 인수를 구분합니다.

MyFunction($Param1, $Param2 $Param3) {
    # ...
}

MyFunction 'one' 'two' 'three' # assigns 'one' to $Param1, 'two' to $Param2, 'three' to $Param3
  • 괄호와 쉼표는 호출(.)에 사용됩니다.) 방법.
  • 쉼표는 배열을 정의하는 데 사용됩니다.MyFunction 'one', 'two', 'three'(또는)MyFunction('one', 'two', 'three')가 배열을 합니다.@('one', 'two', 'three')첫 번째 매개 변수($Param1).
  • 괄호는 포함된 내용을 메모리에 단일 컬렉션으로 삽입하고(PowerShell 파이프라인을 조임), 예를 들어 다음과 같은 임베디드 함수를 호출하는 경우에만 사용해야 합니다.
MyFunction (MyOtherFunction) # passes the results MyOtherFunction to the first positional parameter of MyFunction ($Param1)
MyFunction One $Two (getThree) # assigns 'One' to $Param1, $Two to $Param2, the results of getThree to $Param3

참고: 텍스트 인수를 인용합니다(단어처럼).one다음 예제에서는 공백이나 특수 문자가 포함된 경우에만 필요합니다.

출력 속성은 파이프라인의 첫 번째 개체를 기반으로 합니다.

PowerShell 파이프라인에서 각 개체는 어셈블리 라인의 워크스테이션에서 개체를 처리하고 전달하는 방법과 유사한 cmdlet(파이프라인 중간에 구현됨)에 의해 처리 및 전달됩니다.즉, 이전 cmdlet(워크스테이션)이 다가오는 항목을 동시에 처리하는 동안 각 cmdlet은 한 번에 하나의 항목을 처리합니다.이렇게 하면 개체가 한 번에 메모리에 로드되지 않으며(메모리 사용량이 적음) 다음 개체가 제공되기 전에 이미 처리될 수 있습니다(또는 존재하기도 함).이 기능의 단점은 예상되는 개체 수에 대한 감시가 없다는 것입니다.
따라서 대부분의 PowerShell cmdlet은 파이프라인의 모든 개체가 첫 번째 개체와 일치하고 일반적으로 동일한 속성을 가지고 있다고 가정하지만 항상 그런 것은 아닙니다.

$List =
    [pscustomobject]@{ one = 'a1'; two = 'a2' },
    [pscustomobject]@{ one = 'b1'; two = 'b2'; three = 'b3' }

$List |Select-Object *
one two
--- ---
a1  a2
b1  b2

처럼, 세 칸인 보다시열은피번째세시▁as▁the열은▁third.three첫 번째 개체에 존재하지 않았고 PowerShell이 두 번째 개체의 존재를 인식하기 전에 이미 결과를 출력하고 있었기 때문에 결과에서 누락되었습니다.
이 동작을 해결하는 방법은 다음과 같은 모든 개체의 속성을 사전에 명시적으로 정의하는 것입니다.

$List |Select-Object one, two, three

one two three
--- --- -----
a1  a2
b1  b2  b3

제안서 참조:#13906 Select-Object에 추가 -UnifyProperties 매개변수

파이프라인이 열립니다.

이 기능은 예상대로라면 유용할 수 있습니다.

$Array = 'one', 'two', 'three'
$Array.Length
3

단일 품목 컬렉션

하지만 혼란스러울 수도 있습니다.

$Selection = $Array |Select-Object -First 2
$Selection.Length
2
$Selection[0]
one

컬렉션이 단일 항목으로 축소된 경우:

$Selection = $Array |Select-Object -First 1
$Selection.Length
3
$Selection[0]
o

설명 파이프라인이 변수에 할당된 단일 항목을 출력할 때 컬렉션으로 할당되지 않습니다(다음과 같은 항목 1개 포함). @('one') 하지만 스칼라 항목(항목 자체, 예:'one').
그 말은 그 재산이.Length입니다.).Count는 더 에 적용됩니다.'one'.length은 이와같과 같습니다.3가 ▁and.$Selection[0]의 첫 'one'[0](문자동일와)와 같음)o가 반환됩니다.

해결 방법이 동작을 해결하려면 배열 하위 표현식 연산자를 사용하여 스칼라 항목을 배열에 강제로 적용할 수 있습니다.

$Selection = $Array |Select-Object -First 1
@($Selection).Length
1
@($Selection)[0]
one

그 경우에 그 사건이$Selection이미 배열입니다. 깊이는 더 이상 증가하지 않습니다. @(@('하나', '둘') 다음 섹션 4b를 참조하십시오. 포함된 컬렉션은 평평합니다.

내장형 배열

어레이(또는 컬렉션)에 다음과 같은 내장형 어레이가 포함된 경우:

$Array = @(@('a', 'b'), @('c', 'd'))
$Array.Count
2

포함된 모든 항목이 파이프라인에서 처리되므로 표시되거나 새 변수에 할당될 때 플랫 배열을 반환합니다.

$Processed = $Array |ForEach-Object { $_ }
$Processed.Count
4
$Processed
a
b
c
d

내장된 어레이를 반복하려면 다음 명령을 사용할 수 있습니다.

foreach ($Item in $Array) { $Item.Count }
2
2

또는 단순 루프:

for ($i = 0; $i -lt $Array.Count; $i++) { $Array[$i].Count }
2
2

산출물 수집

컬렉션은 일반적으로 파이프라인에 배치될 때 전개됩니다.

function GetList {
   [Collections.Generic.List[String]]@('a', 'b')
}
(GetList).GetType().Name
Object[]

컬렉션을 단일 항목으로 출력하려면 쉼표 연산자를 사용합니다.

function GetList {
   ,[Collections.Generic.List[String]]@('a', 'b')
}
(GetList).GetType().Name
List`1

5. $Null 연산자의 .

이 gotcha는 이 비교 연산자 기능과 관련이 있습니다.

연산자의 입력이 스칼라 값이면 연산자는 부울 을 반환합니다.입력이 집합인 경우 연산자는 식의 오른쪽 값과 일치하는 집합의 요소를 반환합니다.컬렉션에 일치하는 항목이 없으면 비교 연산자는 빈 배열을 반환합니다.

이는 스칼라에 대한 의미입니다.

'a'   -eq 'a'   # returns $True
'a'   -eq 'b'   # returns $False
'a'   -eq $Null # returns $False
$Null -eq $Null # returns $True

수집의 경우 일치하는 요소가 반환되어 참 또는 거짓 조건으로 평가됩니다.

'a',   'b',   'c'   -eq 'a'   # returns 'a' (truthy)
'a',   'b',   'c'   -eq 'd'   # returns an empty array (falsy)
'a',   'b',   'c'   -eq $Null # returns an empty array (falsy)
'a',   $Null, 'c'   -eq $Null # returns $Null (falsy)
'a',   $Null, $Null -eq $Null # returns @($Null, $Null) (truthy!!!)
$Null, $Null, $Null -eq $Null # returns @($Null, $Null, $Null) (truthy!!!)

즉, 변수가 다음과 같은지 여부를 확인하는 것입니다.$Null 다중이 )$Null, 등식 비교 연산자의 LHS(왼쪽)에 놓습니다.

if ($Null -eq $MyVariable) { ...

괄호 및 할당이 파이프라인을 질식시킵니다.

PowerShell 파이프라인은 단순히 파이프라인 운영자에 의해 연결된 일련의 명령이 아닙니다.|)(ASCII 124).cmdlet 시퀀스를 통해 개별 객체를 동시에 스트리밍하는 개념입니다.강력한 개발 지침에 따라 cmdlet(또는 함수)이 작성되어 파이프라인 중간에 구현되면 파이프라인에서 각 개체를 가져와 처리하고 파이프라인의 다음 개체를 가져와 처리하기 직전에 다음 cmdlet에 결과를 전달합니다.즉, 단순 파이프라인의 경우 다음과 같습니다.

Import-Csv .\Input.csv |Select-Object -Property Column1, Column2 |Export-Csv .\Output.csv

를 cmdlet 파일에 쓸 때.\Output.csv, 더일Select-Object와 cmdlet의 합니다.Import-Csv 개를읽다습에서 ..\input.csv파일(다음 항목 참조:Powershell에 있는 파이프라인).이렇게 하면 메모리 사용량(특히 처리할 개체/레코드가 많은 경우)이 낮게 유지되므로 처리량이 더 빨라질 수 있습니다.파이프라인을 용이하게 하기 위해 PowerShell 개체는 각 개체에 모든 속성 정보(예: 속성 이름)가 포함되어 있기 때문에 상당히 뚱뚱합니다.
따라서 아무 이유 없이 파이프라인을 질식시키는 것은 좋은 관행이 아닙니다.파이프라인을 질식시키는 두 가지 시나리오가 있습니다.

  1. 괄호(예:
(Import-Csv .\Input.csv) |Select-Object -Property Column1, Column2 |Export-Csv .\Output.csv

도대체 어디서.\Input.csv된 후 PowerShell에 전달됩니다.Select-Objectsmdlet.

  1. 할당(예:
$Objects = Import-Csv .\Input.csv
$Objects |Select-Object -Property Column1, Column2 |Export-Csv .\Output.csv

도대체 어디서.\Input.csv되며 PowerShell의 PowerShell에 저장됩니다.$Objects(기억도) 그것을 전달하기 전에.Select-Objectsmdlet.

연산자+= 수 있습니다.

증가 할당 연산자()+=는 증가하기 위한 통사적 설탕이며 원시 요소를 .예를 들어 .$a += $b$a됨 됨할$b + 1새할 수 . ( " " " " " " 증 " 추 " 하 " 데 " 또 " 사 " 수 " 니 " 습 " 는 " 있 " 될 " 다 " 다 " 음 " 에 " 가 " 용 " 할 " 는 " 가 " 당 " 연 " 을 " 산 " 자 " 는 " 목 " 항 " 에 " 션 " 렉 " 또 새 " 한 " 컬String 및 형및hash tables그러나 각 반복에 따라 비용이 증가함에 따라(수집의 크기) 상당히 비싸질 수 있습니다.그 이유는 배열 컬렉션으로서의 개체는 불변이며 오른쪽 변수는 단순히 추가된 것이 아니라 *가 추가되어 왼쪽 변수에 다시 할당되었기 때문입니다.자세한 내용은 증가 할당 연산자()+=를 사용하여 집합을 만들지 않도록 하십시오.

Get-Content은 별도의 linescmdlet을합니다.

많은 (내부 및 외부) cmdlet이 존재한다는 것을 알고 있기 때문에 아마도 cmdlet gotchas가 훨씬 더 많을 것입니다.엔진과 관련된 Gotchas와는 달리 이러한 Gotchas는 종종 발생할 때(예: 경고) 강조하기가 더 쉽습니다(예: 예상치 못한 ConvertTo-Json 결과 참조). 답변: 기본값 - 깊이 2) 또는 "수정"이 있습니다.그러나 모든 것(파일의 전체 내용)을 한 번에 전달하는 것이 아니라 스트리밍 객체(이 경우 줄)에 대한 PowerShell의 일반적인 개념을 엄격하게 적용하는 매우 고전적인 겟체인이 있습니다.

Get-Content .\Input.txt -Match '\r?\n.*Test.*\r?\n'

기본적으로 작동하지 않습니다.Get-Contents각 개체에 단일 문자열(줄 바꿈이 없는 줄)이 포함된 개체 스트림을 반환합니다.

(Get-Content .\Input.txt).GetType().Name
Object[]
(Get-Content .\Input.txt)[0].GetType().Name
String

사실:

Get-Content .\Input.txt -Match 'Test'

단어가 포함된 모든 줄을 반환합니다.Test을 서로라고 .Get-Contents파이프라인에 모든 라인을 배치하고 입력이 집합인 경우 연산자는 식의 오른쪽 값과 일치하는 집합의 요소를 반환합니다.

참고: PowerShell 버전 3부터Get-Contents을 가지고 있습니다.-Raw을 한 변수,즉 다음을 의미합니다.Get-Content -Raw .\Input.txt -Match '\r?\n.*Test.*\r?\n'전체 파일을 메모리에 로드할 때 작동합니다.

다음은 최근에 발견한 내용입니다(PowerShell 2.0 CTP).

$items = "item0", "item1", "item2"

$part = ($items | select-string "item0")

$items = ($items | where {$part -notcontains $_})

당신은 대본의 마지막에 $206가 있다고 생각합니까?

"항목1", "항목2"를 예상하고 있었는데, $항목의 값은 "항목0", "항목1", "항목2"입니다.

다음 XML 파일이 있다고 가정합니다.

<Root>
    <Child />
    <Child />
</Root>

실행:

PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS > @($myDoc.SelectNodes("/Root/Child")).Count
2
PS > @($myDoc.Root.Child).Count
2

이제 XML 파일에 하위 노드가 없고 루트 노드만 있도록 편집한 후 다음 명령문을 다시 실행합니다.

PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS > @($myDoc.SelectNodes("/Root/Child")).Count
0
PS > @($myDoc.Root.Child).Count
1

노드 컬렉션이 실제로 존재하는 경우에만 를 사용하여 노드 컬렉션을 반복하려는 경우 이 1은 성가시게 됩니다.이렇게 해서 XML 핸들러의 속성(점) 표기법을 간단한 바로 가기로 사용할 수 없다는 것을 알게 되었습니다.SelectNodes는 0 컬렉션을 반환합니다.@'ed의 경우 XPathNodeList에서 Object[](GetType() 선택)으로 변환되지만 길이는 유지됩니다.동적으로 생성된 $myDoc.루트.하위 속성(기본적으로 존재하지 않음)은 $null을 반환합니다.$null을 @'ed로 지정하면 길이 1의 배열이 됩니다.

기능 설정...

  • 사용과 관련하여 함수에서 파이프라인 입력을 처리하는 세부 사항$_또는$input 그고그관련여하과것리▁to▁the▁respect▁with▁and여관.begin,process,그리고.end블록
  • 직접 입력과 파이프라인 입력 모두에 대해 함수에 전달되는 6가지 주요 동등성 클래스(입력 없음, null, 빈 문자열, 스칼라, 목록 및/또는 빈 목록)를 처리하고 원하는 것을 얻는 방법.
  • 함수에 여러 인수를 보내기 위한 올바른 호출 구문입니다.

저는 Simple-Talk.com 기사 Down the Rabbit Hole - PowerShell Pipeline, FunctionsParameters에 대한 연구 - 다음과 같은 세 가지 인수를 사용하는 함수에 대한 다양한 호출 구문 함정을 보여주는 개요를 제공합니다.function syntax pitfalls


모듈...

이러한 점은 제 Simple-Talk.com 기사 Rabbit Hole: PowerShell Modules and Encapsulation에서 자세히 설명합니다.

  • 상대 경로를 사용하여 스크립트 내의 파일을 도트 소싱하는 것은 스크립트가 있는 디렉토리가 아니라 현재 디렉토리에 상대적입니다!스크립트에 상대적으로 적용하려면 이 기능을 사용하여 스크립트 디렉토리를 찾습니다. [Update for PowerShell V3+: 그냥 기본 제공 변수를 사용하세요!]

    function Get-ScriptDirectory
    { Split-Path $script:MyInvocation.MyCommand.Path }
    
  • 모듈은 다음과 같이 저장해야 합니다....Modules\name\name.psm1또는...\Modules\any_subpath\name\name.psm1 즉, ▁use다▁just만 사용할 수는 없습니다....Modules\name.psm1모듈의 직접 상위 이름은 모듈의 기본 이름과 일치해야 합니다.는 이 위반했을 .

module naming requirements


2015.06.25 추락 기준 차트

Simple-Talk.com 은 PowerShell의 함정에 대한 저의 세 가지 심층 기사 중 마지막 기사를 출판했습니다.처음 두 부분은 선택한 함정 그룹을 감상하는 데 도움이 되는 퀴즈 형식으로 되어 있습니다. 마지막 부분은 가장 일반적인 함정 36개(일부는 이 페이지의 답변에서 수정됨)가 포함된 월차트입니다.자세한 내용은 여기를 참조하십시오.

Powershell을 염두에 두고 빌드되지 않은 유틸리티의 명령줄을 구축하는 몇 가지 요령이 있습니다.

  • 이름이 숫자로 시작하는 실행 파일을 실행하려면 앰퍼샌드(&)로 앞에 붙입니다.

& 7zip.exe

  • 경로의 임의의 위치에 공백이 있는 실행 파일을 실행하려면 앰퍼샌드(&)로 앞에 붙이고 문자열처럼 따옴표로 묶습니다.즉, 변수의 문자열도 실행할 수 있습니다.

# Executing a string with a space. & 'c:\path with spaces\command with spaces.exe'

# Executing a string with a space, after first saving it in a variable. $a = 'c:\path with spaces\command with spaces.exe' & $a

  • 매개 변수와 인수는 레거시 유틸리티에 위치적으로 전달됩니다.따라서 유틸리티에서 기대하는 방식으로 인용하는 것이 중요합니다.일반적으로 공백이 포함되어 있거나 문자, 숫자 또는 대시(-)로 시작하지 않는 경우 따옴표로 묶습니다.

C:\Path\utility.exe '/parameter1' 'Value #1' 1234567890

  • 변수를 사용하여 공백이나 특수 문자를 포함하는 문자열 값을 전달할 수 있습니다.

$b = 'string with spaces and special characters (-/&)' utility.exe $b

  • 또는 배열 확장을 사용하여 값을 전달할 수도 있습니다.

$c = @('Value #1', $Value2) utility.exe $c

  • Powershell에서 응용 프로그램이 완료될 때까지 기다리려면 출력을 어떤 것에 파이프로 연결하거나 Start-Process를 사용하여 출력을 소비해야 합니다.

# Saving output as a string to a variable. $output = ping.exe example.com | Out-String

# Piping the output. ping stackoverflow.com | where { $_ -match '^reply' }

# Using Start-Process affords the most control. Start-Process -Wait SomeExecutable.com

  • 출력을 표시하는 방식 때문에 일부 명령줄 유틸리티는 Powershell_ 내부에서 실행될 때 중단되는 것으로 나타납니다.ISE.exe(특히 사용자의 입력을 기다리는 경우)이러한 유틸리티는 일반적으로 Powershell.exe 콘솔에서 실행할 때 잘 작동합니다.

알렉스2k8, 당신의 예는 다음과 같습니다.

# -----------------------------------
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 값)을 반환합니다.인수의 순서를 반대로 하면 배열 비교가 중지됩니다.

이 기능은 특정 상황에서 매우 유용하지만 PowerShell의 어레이 처리와 마찬가지로 주의해야 합니다.

기능 'foo'와 'bar'는 동일하게 보입니다.

function foo() { $null  }
function bar() { }

예.

(foo) -eq $null
# True

(bar) -eq $null
# True

그러나:

foo | %{ "foo" }
# Prints: foo

bar | %{ "bar" }
# PRINTS NOTHING

$null을 반환하고 아무것도 반환하지 않는 것은 파이프를 처리하는 것과 같지 않습니다.


이건 키스 힐의 에서 영감을 얻은 겁니다

function bar() {}

$list = @(foo)
$list.length
# Prints: 0

# Now let's try the same but with a temporal variable.
$tmp = foo
$list = @($tmp)
$list.length
# Prints: 1

또 다른 하나:

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

연산자 우선 순위 때문에 $b에는 25가 없습니다. 명령은 ($x,$y)*5와 동일합니다.

$a,$b = $x,($y*5)

논리 연산자와 비트 연산자는 표준 우선 순위 규칙을 따르지 않습니다.연산자는 - 그리고 -보다 더 높은 우선 순위를 가져야 하지만 - 또는 -는 엄격하게 왼쪽에서 오른쪽으로 평가됩니다.

예를 들어 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

효과가 있습니다.하지만 여러분이 생각하는 방식으로는 거의 확실하지 않습니다.

PS> $a = 42;
PS> [scriptblock]$b = { $a }
PS> & $b
42

이것은 전에 $o를 사용하여 저를 넘어뜨린 적이 있습니다.$($o)여야 하는 일부 속성입니다.일부 속성).

# $x is not defined
[70]: $x -lt 0
True
[71]: [int]$x -eq 0
True

x달러가 뭐죠?

제가 최근에 만난 또 다른 것은 파이프라인 입력을 수락하는 [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
    }
}

편집 - 사용하기 시작한 더 나은 해결 방법...

사용자 정의 유형 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
    }
}

$_가 블록에서 덮어쓰기된다는 것을 잊어버리자 몇 번 혼란에 빠졌고, 마찬가지로 여러 reg-ex 매치와 $matches 어레이에 대해서도 머리를 긁적였습니다.>.<

가져온 데이터 테이블에서 pcustom 개체를 숫자로 명시적으로 입력하여 올바르게 정렬할 수 있도록 해야 합니다.

$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 }}

PSC:\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

내 것은 둘 다 파일 복사와 관련이 있습니다...


이전에 다음을 사용하여 매우 크고 복잡한 폴더 구조를 이동해야 했습니다.Move-Item -Path C:\Source -Destination C:\Dest프로세스가 끝날 때에도 소스 디렉터리에는 여전히 많은 파일이 있었습니다.저는 나머지 모든 파일의 이름에 대괄호가 있는 것을 발견했습니다.

문제는 그것이-Path매개 변수는 대괄호를 와일드카드로 처리합니다.
을 Log200에 다음과 는 다음과 Log001을 Log200에 복사하려는 경우 다음과 같이 대괄호를 사용할 수 있습니다.Move-Item -Path C:\Source\Log[001-200].log.

되는 것을 는 나우경대, 와나괄다로해드야위석해, 를 사용했어야 했습니다.-LiteralPath 명령어

ErrorAction 는 기본 설정입니다.
$ErrorActionPreference는 사시변 무시니다됩가를 할 때 됩니다.Move-Item그리고.Copy-Item-Verbose매개 변수

프로세스의 종료 코드를 부울로 처리합니다.

예, 다음 코드를 사용합니다.

$p = Start-Process foo.exe -NoNewWindow -Wait -PassThru
if ($p.ExitCode) {
  # handle error
}

foo.를 한 은 좋습니다exe가 존재하지 않거나 실행에 실패하지 않는 한 상황은 좋습니다. 그런 경우.$p▁▁be 될 것입니다.$null,그리고.[bool]($null.ExitCode)거짓입니다.

를 간한해결논대것체입니다는하리를로 바꾸는 입니다.if ($p.ExitCode -ne 0) {}이 더 낫습니다: 나코드의 imo명확성을위해다음더좋다습니이그.if (($p -eq $null) -or ($p.ExitCode -ne 0)) {}

언급URL : https://stackoverflow.com/questions/803521/powershell-pitfalls

반응형