PowerShell
본 시리즈에서는 PowerShell의 구체적인 사용에 대해 정리한다.
다음 게시글: OpenFileDialog / SaveFileDialog 사용하기
C#의 using 키워드 구현하기
C#은 System.IDisposable
인터페이스를 구현하는 객체에 대해 using
구문을 사용하여 블록을 벗어날 때 자동으로 관리되지 않는 리소스들을 해제할 수 있다. 예를 들어,
using (FileStream fileStream = new FileStream()) {
// Do Something
}
와 같은 구문을 통해 파일 스트림 작업이 끝나고 블록을 벗어나면 자동으로 fileStream 내부의 리소스들이 해제될 수 있다.
.NET Framework의 구성 요소들을 사용할 수 있는 PowerShell에는 기본적으로 using
statement가 포함되어 있지는 않지만 다음과 같이 간단하게 함수를 하나 선언하여 이를 구현할 수는 있다
전체적인 코드는 다음과 같다.
function Using-Disposable {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[AllowNull()]
[System.IDisposable]
$disposable,
[Parameter(Mandatory = $true)]
[scriptblock]
$scriptBlock
)
begin {
} process {
.$scriptBlock
} end {
if ($disposable -ne $null) {
$disposable.Dispose()
}
}
}
#usage
Using-Disposable($fileStream = New-Object System.IO.FileStream("hello.txt")) {
// Do Something
$fileStream.Flush()
}
위 코드를 분해하여 이해해본다.
function Using-Disposable {
함수의 이름이다. Using-Disposable
이외의 다른 이름을 얼마든지 지정 가능하다.
[CmdletBinding()]
함수에 부여할 수 있는 특성(attribute)이다. 이 특성이 부여되면 마치 PowerShell에 기본으로 내장된 명령인 cmdlet처럼 코딩하여 호출 가능하게 된다.
param (
이 함수가 받는 매개변수들을 선언한다.
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[AllowNull()]
[System.IDisposable]
$disposable,
첫 번째 매개변수는 using
statement에 사용될 IDisposable
객체이다. 여기에 부여되는 특성은 이름에서 볼 수 있듯,
생략할 수 없는 필요 매개변수([Parameter(Mandatory = $true)]
)이고,
""
또는 [System.String]::Empty
같은 빈 문자열이 전달될 수 있고([AllowEmptyString()]
),
포함하고 있는 원소의 개수가 0
개인 빈 콜렉션이 전달될 수 있고([AllowEmptyCollection()]
),
아예 null
객체가 전달될 수도 있다([AllowNull()]
).
다만, 이 매개변수의 데이터타입은 반드시 System.IDisposable
이다.
$disposable
은 매개변수의 이름이므로 자유롭게 수정 가능하다.
[Parameter(Mandatory = $true)]
[scriptblock]
$scriptBlock
Using-Disposable
이 블록 { }
으로 감싸게 될 많은 실행 구문들이 "매개변수"의 형태로 이 곳에 전달된다. 그러므로 이 매개변수의 데이터 타입은 scriptblock
이다. C#의 방식으로는 람다식? 정도로 이해할 수 있다. 스크립트 언어이기에 가능한 기능이다.
이 매개변수는 직관적으로 보아도 당연히 생략될 수 없다([Parameter(Mandatory = $true)]]
).
$scriptBlock
은 매개변수의 이름이므로 자유롭게 수정 가능하다.
PowerShell의 함수는 다음과 같은 구조로 선언할 수 있다.
function 함수이름 {
param(매개변수, 매개변수, ...)
begin { 준비작업 }
process { 본문 }
end { 정리작업 }
반환할 값 또는 객체
}
PowerShell 함수에서는 값을 반환할 때 return
과 같은 키워드가 없고 그냥 값 그 자체를 함수 끝에 적어주면 되는 것이 인상적이다. 또한 함수를 작성할 때 준비작업과 본문 및 정리작업을 각각 블록으로 구분하여서 적을 수 있는 것도 인상적인데, C++이나 C#에는 이러한 구문이 없지만 굳이 빗대자면 예외처리를 할 때 try
, finally
를 사용하는 것 정도로 이해해할 수 있다. 이보다 더 좋은 비유가 있다면 댓글로 남겨주시길...
실행되는 순서는 당연하게도 begin
블록 안의 내용이 실행되고 나서 process
블록 안의 내용이 실행이 될 것이고, 마지막으로 end
블록 안의 내용이 실행될 것이다.
begin {
} process {
.$scriptBlock
} end {
if ($disposable -ne $null) {
$disposable.Dispose()
}
}
어쨌든, 이 함수는 사전 작업 할 것이 딱히 없으므로 begin
블록은 비워 두고 process
블록에서 앞서 매개변수로 전달받은 블록인 $scriptBlock
을 실행한다.
스크립트 블록의 실행이 끝나면 Dispose()
를 호출한다. PowerShell의 조건 연산자는 C#, C++과는 판이하게 다르며 오히려 perl과 가깝다. -ne
는 !=
연산자와 같다. 매개변수로 받은 $disposable
가 null
이 아니라면 Dispose
메소드를 호출한다.
Using-Disposable($fileStream = New-Object System.IO.FileStream("hello.txt")) {
// Do Something
$fileStream.Flush()
}
이제 실제로 사용해 본다.
첫 번째 매개변수였던 $disposable
에는 System.IO.FileStream
형 객체가 전달된다. 그리고 블록으로 감싼 코드는 두 번째 매개변수였던 $scriptBlock
으로 전달된다.
$fileStream.Flush()
까지 실행이 끝나고 블록을 벗어날 때, Using-Disposable
의 end
블록에 적었던 Dispose
메소드가 비로소 호출되고 메모리가 정리된다.