다음을 통해 공유


$null 대해 알고 싶었던 모든 것

PowerShell $null 종종 간단해 보이지만 뉘앙스가 많이 있습니다. $null을 자세히 살펴보아, $null 값이 예기치 않게 실행될 때 어떻게 되는지 알 수 있도록 하겠습니다.

비고

이 문서의 원래 버전@KevinMarquette작성한 블로그에 나타났습니다. PowerShell 팀은 이 콘텐츠를 공유해 주신 Kevin에게 감사드립니다. PowerShellExplained.com자신의 블로그를 확인하세요.

NULL이란?

NULL을 알 수 없거나 빈 값으로 생각할 수 있습니다. 변수는 값 또는 개체를 할당할 때까지 NULL입니다. 값이 NULL인 경우 값을 필요로 하고 오류를 생성하는 몇 가지 명령이 있기 때문에 이 작업이 중요할 수 있습니다.

PowerShell $null

$null NULL을 나타내는 데 사용되는 PowerShell의 자동 변수입니다. 변수에 할당하고, 비교에 사용하고, 컬렉션에서 NULL의 자리 표시자로 사용할 수 있습니다.

PowerShell은 값이 NULL인 개체로 $null 처리합니다. 이는 다른 언어에서 온 경우 예상한 것과 다릅니다.

$null의 예시들

초기화하지 않은 변수를 사용하려고 하면 값이 $null. 이는 $null 값이 코드에 몰래 들어오는 가장 일반적인 방법 중 하나입니다.

PS> $null -eq $undefinedVariable
True

변수 이름을 잘못 입력하면 PowerShell에서 변수 이름을 다른 변수로 보고 값이 $null.

$null 값을 찾는 다른 방법은 결과를 제공하지 않는 다른 명령에서 가져올 때입니다.

PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True

$null의 영향

$null 값은 표시되는 위치에 따라 코드에 다르게 영향을 줍니다.

문자열에서

문자열에서 $null 사용하는 경우 빈 값(또는 빈 문자열)입니다.

PS> $value = $null
PS> Write-Output "'The value is $value'"
'The value is '

이것이 로그 메시지에서 사용할 때 변수 주위에 대괄호를 배치하는 이유 중 하나입니다. 값이 문자열 끝에 있을 때 변수 값의 가장자리를 식별하는 것이 더욱 중요합니다.

PS> $value = $null
PS> Write-Output "The value is [$value]"
The value is []

이렇게 하면 빈 문자열과 $null 값을 쉽게 발견할 수 있습니다.

숫자 수식에서

숫자 수식에 $null 값을 사용하면 오류가 표시되지 않으면 결과가 유효하지 않습니다. 때로는 $null이(가) 0로 평가되며, 다른 경우에는 전체 결과가 $null가 됩니다. 다음은 값의 순서에 따라 0 또는 $null 제공하는 곱하기 예제입니다.

PS> $null * 5
PS> $null -eq ( $null * 5 )
True

PS> 5 * $null
0
PS> $null -eq ( 5 * $null )
False

컬렉션을 대신하여

컬렉션을 사용하면 인덱스를 사용하여 값에 액세스할 수 있습니다. 실제로 null컬렉션에 인덱싱하려고 하면 다음 오류가 발생합니다. Cannot index into a null array.

PS> $value = $null
PS> $value[10]
Cannot index into a null array.
At line:1 char:1
+ $value[10]
+ ~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

컬렉션이 있지만 컬렉션에 없는 요소에 액세스하려고 하면 $null 결과가 표시됩니다.

$array = @( 'one','two','three' )
$null -eq $array[100]
True

개체 대신

지정한 속성이 없는 개체의 속성 또는 하위 속성에 액세스하려고 하면 정의되지 않은 변수와 마찬가지로 $null 값이 표시됩니다. 이 경우 변수가 $null이든 실제 개체이든 중요하지 않습니다.

PS> $null -eq $undefined.Some.Fake.Property
True

PS> $date = Get-Date
PS> $null -eq $date.Some.Fake.Property
True

null 값 식에 대한 메서드

$null 개체에서 메서드를 호출하면 RuntimeException이 발생합니다.

PS> $value = $null
PS> $value.ToString()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $value.ToString()
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression 구를 볼 때마다, 변수에서 메서드를 호출하기 전에 먼저 $null을 확인하지 않은 위치를 찾습니다.

$null 값 확인

예제에서 $null을 확인할 때 제가 항상 $null를 왼쪽에 배치한다는 것을 눈치채셨을지도 모릅니다. 이는 의도적이며 PowerShell 모범 사례로 허용됩니다. 오른쪽에 배치해도 예상 결과를 얻지 못하는 몇 가지 시나리오가 있습니다.

다음 예제를 살펴보고 결과를 예측해 보세요.

if ( $value -eq $null )
{
    'The array is $null'
}
if ( $value -ne $null )
{
    'The array is not $null'
}

내가 $value을 정의하지 않으면, 첫 번째는 $true 로 평가되고, 우리의 메시지는 The array is $null입니다. 여기서 함정은 둘 다 $value될 수 있도록 하는 $false를 만들 수 있다는 것입니다.

$value = @( $null )

이 경우 $value$null포함하는 배열입니다. -eq 배열의 모든 값을 확인하고 일치하는 $null 반환합니다. 이 값은 $false로 평가됩니다. -ne$null과 일치하지 않는 모든 항목을 반환하며, 이 경우 결과가 없습니다 (이것은 $false로 평가됩니다). 둘 다 $true이 아니지만, 이 중 하나가 $true일 것처럼 보입니다.

둘 다 $false로 평가되는 값을 만들 수 있을 뿐만 아니라, 둘 다 $true로 평가되는 값도 만들 수 있습니다. 마티아스 제센(@IISResetMe)은 이 시나리오를 자세히 살펴보는 좋은 포스트를 가지고 있습니다.

PSScriptAnalyzer 및 VS Code

PSScriptAnalyzer 모듈에는 PSPossibleIncorrectComparisonWithNull이라는 이 문제를 확인하는 규칙이 있습니다.

PS> Invoke-ScriptAnalyzer ./myscript.ps1

RuleName                              Message
--------                              -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.

VS Code는 PSScriptAnalyser 규칙도 사용하므로 스크립트에서 이를 강조 표시하거나 식별합니다.

if문 단순 확인

비 $null 값을 확인하는 일반적인 방법은 비교 없이 간단한 if() 문을 사용하는 것입니다.

if ( $value )
{
    Do-Something
}

값이 $null인 경우 이것은 $false로 평가됩니다. 읽기 쉽지만 원하는 내용을 정확히 찾고 있는지 주의해야 합니다. 코드 줄을 다음과 같이 읽었습니다.

$value에 값이 있는 경우

그러나 그것은 전체 이야기가 아닙니다. 그 줄은 실제로 다음과 같이 말하고 있습니다.

$value$null, 0, $false, 빈 문자열 또는 빈 배열이 아닌 경우.

다음은 해당 진술의 보다 더 완전한 샘플입니다.

if ( $null -ne $value -and
        $value -ne 0 -and
        $value -ne '' -and
        ($value -isnot [array] -or $value.Length -ne 0) -and
        $value -ne $false )
{
    Do-Something
}

변수에 값이 있는 것뿐만 아니라 다른 값들이 if로 계산된다는 점을 기억하는 한, 기본 $false 검사를 사용하는 것이 완전히 괜찮습니다.

며칠 전에 일부 코드를 리팩터링할 때 이 문제가 발생했습니다. 그것은 다음과 같은 기본 속성 검사를 했다.

if ( $object.Property )
{
    $object.Property = $value
}

개체 속성이 있는 경우에만 값을 할당하려고 했습니다. 대부분의 경우, 원래 개체는 $true 문에서 if로 평가되는 값을 가지고 있었습니다. 그러나 값이 때때로 설정되지 않는 문제가 발생했습니다. 코드를 디버깅하고 개체에 속성이 있지만 빈 문자열 값인 것을 발견했습니다. 이로 인해 이전 논리로는 결코 업데이트되지 않았습니다. 그래서 나는 적절한 $null 검사를 추가했더니 모든 것이 제대로 작동했다.

if ( $null -ne $object.Property )
{
    $object.Property = $value
}

이러한 작은 버그들은 발견하기 어려워서 $null의 값을 적극적으로 확인하게 만듭니다.

$null. 세다

$null 값의 속성에 액세스하려고 하면 속성 자체가 $null임을 볼 수 있습니다. Count 속성은 이 규칙의 예외입니다.

PS> $value = $null
PS> $value.Count
0

$null 값이 있으면 Count0입니다. 이 특수 속성은 PowerShell에서 추가됩니다.

[PSCustomObject] 개수

PowerShell의 거의 모든 개체에는 해당 Count 속성이 있습니다. 한 가지 중요한 예외는 Windows PowerShell 5.1의 [pscustomobject](PowerShell 6.0에서 수정됨)입니다. Count 속성이 없으므로 사용하려는 경우 $null 값을 가져옵니다. 여기에서 말씀드리는 이유는 Count 검사 대신 $null를 사용하지 않도록 하기 위함입니다.

Windows PowerShell 5.1 및 PowerShell 6.0에서 이 예제를 실행하면 다양한 결과를 얻을 수 있습니다.

$value = [pscustomobject]@{Name='MyObject'}
if ( $value.Count -eq 1 )
{
    "We have a value"
}

열거할 수 있는 null

다른 유형과 다르게 작동하는 특별한 유형의 $null 있습니다. 열거 가능한 null이라고 부르지만, 실제로는 System.Management.Automation.Internal.AutomationNull입니다. 이 열거 가능한 null은 아무 것도 반환하지 않는 함수 또는 스크립트 블록의 결과로 얻을 수 있는 null입니다(void 결과).

PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True

$null과(와) 이를 비교하면 $null 값을 얻습니다. 값이 필요한 평가에서 사용되는 경우 값은 항상 $null. 그러나 배열 내에 배치하면 빈 배열과 동일하게 처리됩니다.

PS> $containEmpty = @( @() )
PS> $containNothing = @($nothing)
PS> $containNull = @($null)

PS> $containEmpty.Count
0
PS> $containNothing.Count
0
PS> $containNull.Count
1

$null 값을 포함한 배열을 가질 수 있으며 그 배열의 Count1입니다. 그러나 배열 내에 빈 배열을 배치하면 항목으로 계산되지 않습니다. 개수는 0.

열거 가능한 null을 컬렉션처럼 처리하면 비어 있습니다.

강력한 형식이 아닌 함수 매개 변수에 열거 가능한 null을 전달하면 PowerShell은 기본적으로 열거 가능한 null을 $null 값으로 강제 변환합니다. 즉, 함수 내에서 값은 $null 형식 대신 처리됩니다.

파이프라인

차이점은 파이프라인을 사용할 때의 주요 위치입니다. $null 값을 파이프할 수 있지만 열거 가능한 null 값은 파이프할 수 없습니다.

PS> $null | ForEach-Object{ Write-Output 'NULL Value' }
'NULL Value'
PS> $nothing | ForEach-Object{ Write-Output 'No Value' }

코드에 따라 $null를 논리에서 고려해야 합니다.

먼저 $null를 확인하거나

  • 파이프라인에서 null을 필터링 (... | where {$null -ne $_} | ...)
  • 파이프라인 함수에서 처리

프로그래밍에서 'foreach'는 특별한 번역 없이 사용됩니다.

foreach에서 제가 가장 선호하는 기능 중 하나는 $null 컬렉션을 열거하지 않는다는 점입니다.

foreach ( $node in $null )
{
    #skipped
}

이렇게 하면 컬렉션을 열거하기 전에 $null 검사할 필요가 없습니다. $null 값 컬렉션이 있는 경우 $node 여전히 $null수 있습니다.

foreach PowerShell 3.0에서 이러한 방식으로 작동하기 시작했습니다. 이전 버전에 있는 경우는 그렇지 않습니다. 이는 2.0 호환성을 위해 코드를 다시 포팅할 때 알아야 할 중요한 변경 사항 중 하나입니다.

값 형식

기술적으로 참조 형식만 $null이 될 수 있습니다. 그러나 PowerShell은 매우 관대하며 변수가 모든 형식이 될 수 있습니다. 값 형식을 강력하게 타입화하려는 경우, $null이 될 수 없습니다. PowerShell은 $null 여러 형식의 기본값으로 변환합니다.

PS> [int]$number = $null
PS> $number
0

PS> [bool]$boolean = $null
PS> $boolean
False

PS> [string]$string = $null
PS> $string -eq ''
True

$null유효한 변환이 없는 몇 가지 형식이 있습니다. 이러한 형식은 Cannot convert null to type 오류를 생성합니다.

PS> [datetime]$date = $null
Cannot convert null to type "System.DateTime".
At line:1 char:1
+ [datetime]$date = $null
+ ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

함수 매개 변수

함수 매개 변수에 강력한 형식의 값을 사용하는 것은 매우 일반적입니다. 일반적으로 스크립트에서 다른 변수의 형식을 정의하지 않는 경향이 있더라도 매개 변수 형식을 정의하는 방법을 배웁니다. 함수에 강력한 형식의 변수가 이미 있을 수 있으며 이를 인식하지 못할 수도 있습니다.

function Do-Something
{
    param(
        [string] $Value
    )
}

매개 변수의 형식을 string로 설정하면, 값을 절대로 $null로 설정할 수 없습니다. 값이 $null인지 확인하여 사용자가 값을 제공했는지 여부를 판단하는 것이 일반적입니다.

if ( $null -ne $Value ){...}

$Value 값이 제공되지 않을 때 '' 빈 문자열입니다. 대신 자동 변수 $PSBoundParameters.Value 사용합니다.

if ( $null -ne $PSBoundParameters.Value ){...}

$PSBoundParameters 함수가 호출될 때 지정된 매개 변수만 포함합니다. ContainsKey 메서드를 사용하여 속성을 확인할 수도 있습니다.

if ( $PSBoundParameters.ContainsKey('Value') ){...}

IsNotNullOrEmpty (영문)

값이 문자열인 경우 정적 문자열 함수를 사용하여 값이 동시에 $null 또는 빈 문자열인지 확인할 수 있습니다.

if ( -not [string]::IsNullOrEmpty( $value ) ){...}

값 형식이 문자열이어야 한다는 것을 알고 있을 때 이를 자주 사용합니다.

$null을 확인할 때

나는 방어 스크립터입니다. 함수를 호출하고 변수에 할당할 때마다 $null확인합니다.

$userList = Get-ADUser kevmar
if ($null -ne $userList){...}

if사용하는 대신 foreach 사용하거나 try/catch 사용하는 것이 좋습니다. 오해하지 마세요, 나는 아직도 try/catch 많이 사용합니다. 그러나 오류 조건 또는 빈 결과 집합을 테스트할 수 있는 경우 예외 처리가 실제 예외가 되도록 허용할 수 있습니다.

또한 개체의 값이나 호출 메서드를 인덱싱하기 전에 $null 확인하는 경향이 있습니다. 이 두 작업은 $null 개체에 대해 실패하므로 먼저 유효성을 검사하는 것이 중요합니다. 이 게시물의 앞부분에서 이러한 시나리오를 이미 다루었습니다.

결과 시나리오 없음

다양한 함수와 명령이 결과 없음 시나리오를 다르게 처리한다는 것을 알아야 합니다. 많은 PowerShell 명령은 열거 가능한 null과 오류 스트림의 오류를 반환합니다. 그러나 다른 것들은 예외를 던지거나 상태 개체를 반환합니다. 명령어가 결과 없음과 오류 시나리오를 어떻게 처리하는지는 여전히 사용자에게 달려 있습니다.

$null로 초기화

내가 집어 들었던 한 가지 습관은 변수를 사용하기 전에 모든 변수를 초기화하는 것입니다. 다른 언어로 이 작업을 수행해야 합니다. 함수의 맨 위에 있거나 foreach 루프를 입력할 때 사용 중인 모든 값을 정의합니다.

다음은 자세히 살펴보려는 시나리오입니다. 전에 쫓아야 했던 버그의 한 예입니다.

function Do-Something
{
    foreach ( $node in 1..6 )
    {
        try
        {
            $result = Get-Something -Id $node
        }
        catch
        {
            Write-Verbose "[$result] not valid"
        }

        if ( $null -ne $result )
        {
            Update-Something $result
        }
    }
}

여기서는 Get-Something 결과 또는 열거 가능한 null을 반환할 것으로 예상합니다. 오류가 발생하면 기록합니다. 그런 다음, 처리하기 전에 유효한 결과를 얻었는지 확인합니다.

이 코드에 숨겨진 버그는 Get-Something가 예외를 발생시키고 $result에 값을 할당하지 않을 때입니다. 할당 전에 실패하므로 $null 변수에 $result 할당하지 않습니다. $result에는 다른 반복의 이전 유효한 $result이 여전히 포함되어 있습니다. 이 예제에서는 동일한 개체를 여러 번 실행하기 위해 Update-Something 합니다.

이 문제를 완화하기 위해 사용하기 전에 $result 루프 내에서 바로 $null을(를) foreach으로 설정했습니다.

foreach ( $node in 1..6 )
{
    $result = $null
    try
    {
        ...

범위 문제

또한 범위 지정 문제를 완화하는 데 도움이 됩니다. 이 예제에서는 루프에서 $result 값을 반복해서 할당합니다. 그러나 PowerShell을 사용하면 함수 외부의 변수 값이 현재 함수의 범위로 스피할 수 있으므로 함수 내에서 초기화하면 이러한 방식으로 도입될 수 있는 버그가 완화됩니다.

부모 범위에서 값이 설정된 경우, 함수의 초기화되지 않은 변수는 $null가 아닙니다. 부모 범위는 함수를 호출하고 동일한 변수 이름을 사용하는 다른 함수일 수 있습니다.

동일한 Do-something 예제를 사용하고 루프를 제거하면 이 예제와 같은 항목이 표시됩니다.

function Invoke-Something
{
    $result = 'ParentScope'
    Do-Something
}

function Do-Something
{
    try
    {
        $result = Get-Something -Id $node
    }
    catch
    {
        Write-Verbose "[$result] not valid"
    }

    if ( $null -ne $result )
    {
        Update-Something $result
    }
}

Get-Something 호출이 예외를 발생시키면, $null 검사는 $result에서 Invoke-Something를 찾습니다. 함수 내에서 값을 초기화하면 이 문제가 완화됩니다.

변수 명명은 어렵고 작성자가 여러 함수에서 동일한 변수 이름을 사용하는 것이 일반적입니다. 나는 $node,$result,$data 항상 사용하는 것을 알고있다. 따라서 다른 범위의 값이 안 되는 위치에 표시되기가 매우 쉽습니다.

출력을 $null로 리디렉션

이 전체 글에서 $null 값에 대해 이야기해 왔지만 $null로 출력을 리디렉션하는 것을 언급하지 않으면 논의가 완료되지 않습니다. 표시하지 않으려는 정보 또는 개체를 출력하는 명령이 있는 경우가 있습니다. 출력을 $null으로 리디렉션하면 해당 작업이 수행됩니다.

Out-Null (결과를 모두 버리는 명령어)

Out-Null 명령은 파이프라인 데이터를 $null로 리디렉션하는 내장된 방법입니다.

New-Item -Type Directory -Path $path | Out-Null

$null에 할당

명령의 결과를 $null에 할당하면 Out-Null을 사용하는 것과 동일한 효과를 얻을 수 있습니다.

$null = New-Item -Type Directory -Path $path

$null 상수 값이므로 덮어쓸 수 없습니다. 코드에서 보이는 방식이 마음에 들지 않지만 Out-Null보다 빠르게 수행하는 경우가 많습니다.

$null로 리디렉션

리디렉션 연산자를 사용하여 출력을 $null보낼 수도 있습니다.

New-Item -Type Directory -Path $path > $null

다른 스트림에서 출력하는 명령줄 실행 파일을 처리하는 경우 다음과 같이 모든 출력 스트림을 $null 리디렉션할 수 있습니다.

git status *> $null

요약

나는 이 글에서 많은 내용을 다루었고, 이 기사가 대체로 내 깊이 있는 분석보다 더 산만하다는 것을 알고 있다. $null 값은 PowerShell 내 여러 위치에서 나타날 수 있으며, 뉘앙스는 그것을 발견한 위치와 관련이 있기 때문입니다. 나는 여러분이 이 경험에서 $null에 대한 더 나은 이해와 마주칠 수 있는 더 모호한 시나리오에 대한 인식을 얻어 가기를 기대합니다.