about_Remote_Variables — 원격 명령에서 지역·원격 변수 사용하기
about_Remote_Variables — 원격 명령에서 지역·원격 변수 사용하기
원격 컴퓨터에서 명령을 실행할 때 변수는 어디에서 정의됐는지가 아주 중요해요. 명령이 실행되는 세션에 변수가 있는지, 아니면 내 컴퓨터(지역 세션)에만 있는지에 따라 다루는 방법이 달라지거든요. 이 글에서는 원격 명령 안에서 지역 변수와 원격 변수를 어떻게 사용하는지, 특히 파워셸 3.0부터 생긴 Using: 스코프 수정자를 중심으로 차근차근 설명할게요.
본문
원격 명령은 원격 컴퓨터에서 실행되는 명령이에요. 그 명령 안에서도 평소처럼 변수를 쓰고, 변수에 값을 할당한 뒤 그 값을 쓸 자리에 변수를 넣을 수 있죠.
문제는 그 변수가 어느 세션에 정의되어 있는가예요. 기본적으로 원격 명령 안의 변수는 그 명령이 실행되는 세션에 정의된 것으로 간주돼요. 즉 지역(내 컴퓨터) 세션에 정의된 변수는, 원격 명령 안에서 쓰려면 그게 지역 변수임을 명령에 따로 알려줘야 해요. 그 방법을 하나씩 볼게요.
원격 변수 사용하기
파워셸은 원격 명령에 쓰인 변수가 명령이 실행되는 세션에 정의되어 있다고 가정해요.
아래 예시의 $ps 변수는 Get-WinEvent 명령이 실행되는 임시 세션에서 정의돼요.
Invoke-Command -ComputerName S1 -ScriptBlock {
$ps = "*PowerShell*"; Get-WinEvent -LogName $ps
}
명령이 **영구 세션(PSSession)**에서 실행될 때는 원격 변수가 그 세션에 정의되어 있어야 해요. 임시 세션과 달리 세션이 계속 유지되니까, 변수도 그 세션 안에서 만들어 두는 거예요.
$s = New-PSSession -ComputerName S1
Invoke-Command -Session $s -ScriptBlock {$ps = "*PowerShell*"}
Invoke-Command -Session $s -ScriptBlock {Get-WinEvent -LogName $ps}
지역 변수 사용하기
원격 명령에서도 지역 변수를 쓸 수 있어요. 다만 변수가 지역 세션에 정의되어 있어야 하죠.
파워셸 3.0부터는 Using: 스코프 수정자를 사용해서 원격 명령 안의 지역 변수를 지정할 수 있어요. 문법은 이렇게 생겼어요.
$Using:<VariableName>
아래 예시에서 $ps 변수는 지역 세션에서 만들어졌지만, 명령이 실행되는 세션에서 사용돼요. Using: 스코프 수정자가 $ps를 지역 변수로 지정해 주는 거예요.
$ps = "*PowerShell*"
Invoke-Command -ComputerName S1 -ScriptBlock {
Get-WinEvent -LogName $Using:ps
}
Using: 스코프 수정자는 PSSession에서도 똑같이 쓸 수 있어요.
$s = New-PSSession -ComputerName S1
$ps = "*PowerShell*"
Invoke-Command -Session $s -ScriptBlock {Get-WinEvent -LogName $Using:ps}
여기서 주의할 점이 하나 있어요. $Using:var 같은 변수 참조는 호출자 컨텍스트에 있는 $var 변수의 값으로 확장돼요. 그렇다고 호출자의 변수 객체 자체에 접근하는 건 아니에요. 그래서 Using: 스코프 수정자로 PSSession 안의 지역 변수 값을 수정할 수는 없어요. 예를 들어 다음 코드는 동작하지 않아요.
$s = New-PSSession -ComputerName S1
$ps = "*PowerShell*"
Invoke-Command -Session $s -ScriptBlock {$Using:ps = 'Cannot assign new value'}
Using:에 대한 자세한 내용은 about_Scopes를 참고하세요.
Using splatting — 스플래팅과 함께 쓰기
파워셸 **스플래팅(splatting)**은 매개 변수 이름과 값의 모음을 한 번에 명령에 전달하는 방법이에요. 자세한 내용은 about_Splatting에서 다룰게요.
이 예시에서 스플래팅 변수 $Splat은 지역 컴퓨터에서 만들어 둔 해시 테이블이에요. Invoke-Command는 원격 컴퓨터 세션에 연결하죠. ScriptBlock 안에서 Using: 스코프 수정자를 골뱅이(@) 기호와 함께 써서 스플래팅된 변수를 나타내요.
$Splat = @{ Name = "Win*"; Include = "WinRM" }
Invoke-Command -Session $s -ScriptBlock { Get-Service @Using:Splat }
Using: 스코프 수정자가 필요한 다른 상황
세션 밖에서 실행되는 스크립트나 명령이라면, 호출 세션 스코프의 변수 값을 심어 주기 위해 Using: 스코프 수정자가 필요해요. 그래야 세션 밖의 코드가 그 값에 접근할 수 있으니까요. Using: 스코프 수정자가 지원되는 상황은 이 세 가지예요.
Invoke-Command를ComputerName,HostName,SSHConnection또는Session매개 변수로 시작한 원격 실행 명령 — 원격 세션Start-Job으로 시작한 백그라운드 작업 — 프로세스 외부 세션(out-of-process session)Start-ThreadJob또는ForEach-Object -Parallel로 시작한 스레드 작업 — 별도 스레드 세션
상황에 따라 심어진 변수 값은 호출자 스코프 데이터의 독립적인 복사본이거나 **참조(reference)**예요. 원격·프로세스 외부 세션에서는 항상 독립 복사본이고, 스레드 세션에서는 참조로 전달돼요.
변수 값의 직렬화
원격 실행 명령과 백그라운드 작업은 프로세스 외부에서 실행돼요. 프로세스 외부 세션은 XML 기반 직렬화·역직렬화를 써서 변수 값을 프로세스 경계 너머에서도 사용할 수 있게 해요. 직렬화 과정은 객체를 PSObject로 변환하는데, 이 PSObject는 원래 객체의 속성(property)은 갖지만 메서드(method)는 없어요.
제한된 유형 집합에서는 역직렬화가 객체를 원래 유형으로 되살려 줘요(rehydrate). 되살아난 객체는 원래 객체 인스턴스의 복사본으로, 유형의 속성과 메서드를 모두 갖고 있어요. System.Version 같은 단순 유형은 복사가 정확해요. 하지만 복잡한 유형은 복사가 불완전할 수 있어요. 예를 들어 되살아난 인증서 객체에는 프라이빗 키가 포함되지 않아요.
그 외 모든 유형의 인스턴스는 PSObject 인스턴스로 남아요. 이때 pstypenames 속성에는 원래 유형 이름 앞에 Deserialized가 붙어요. 예를 들면 Deserialized.System.Data.DataTable처럼요.
ArgumentList 매개 변수로 지역 변수 사용하기
Using:과는 다른 방법도 있어요. 원격 명령에 매개 변수를 정의하고, Invoke-Command cmdlet의 ArgumentList 매개 변수로 지역 변수를 그 매개 변수 값으로 넘기는 방식이에요.
param 키워드로 원격 명령의 매개 변수를 정의해요. 이때 매개 변수 이름은 자리 표시자라서 지역 변수 이름과 꼭 일치할 필요가 없어요.
param키워드로 정의한 매개 변수를 명령 안에서 사용해요.Invoke-Commandcmdlet의ArgumentList매개 변수로 지역 변수를 매개 변수 값으로 지정해요.
예를 들어 아래 명령들은 지역 세션에서 $ps 변수를 정의하고 그걸 원격 명령에서 사용해요. 명령은 $Log를 매개 변수 이름으로 쓰고, 지역 변수 $ps를 그 값으로 넘기죠.
$ps = "*PowerShell*"
Invoke-Command -ComputerName S1 -ScriptBlock {
param($Log)
Get-WinEvent -LogName $Log
} -ArgumentList $ps