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
값이 있으면 Count
은 0
입니다. 이 특수 속성은 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
값을 포함한 배열을 가질 수 있으며 그 배열의 Count
는 1
입니다. 그러나 배열 내에 빈 배열을 배치하면 항목으로 계산되지 않습니다. 개수는 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
에 대한 더 나은 이해와 마주칠 수 있는 더 모호한 시나리오에 대한 인식을 얻어 가기를 기대합니다.
PowerShell