Language/PowerShell | 2020. 9. 15. 12:11

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!= 연산자와 같다. 매개변수로 받은 $disposablenull이 아니라면 Dispose 메소드를 호출한다.

 


Using-Disposable($fileStream = New-Object System.IO.FileStream("hello.txt")) {
    // Do Something
    $fileStream.Flush()
}

이제 실제로 사용해 본다.

첫 번째 매개변수였던 $disposable에는 System.IO.FileStream형 객체가 전달된다. 그리고 블록으로 감싼 코드는 두 번째 매개변수였던 $scriptBlock으로 전달된다.

$fileStream.Flush()까지 실행이 끝나고 블록을 벗어날 때, Using-Disposableend 블록에 적었던 Dispose 메소드가 비로소 호출되고 메모리가 정리된다.

댓글