about_Jobs — PowerShell 백그라운드 작업
about_Jobs — PowerShell 백그라운드 작업
PowerShell에서 오래 걸리는 명령이나 스크립트를 그냥 실행하면 그동안 터미널을 못 쓰잖아요. 그럴 때 **백그라운드 작업(background job)**이 필요해요. 백그라운드 작업은 명령을 현재 세션과 분리된 뒤에서 실행해서, 작업이 도는 동안에도 프롬프트로 계속 다른 일을 할 수 있게 해 줘요. 이 문서에서는 작업이 뭔지, 세 가지 작업 유형이 어떻게 다른지, 그리고 Start-Job부터 정리까지 작업을 다루는 cmdlet들을 어떻게 쓰는지 하나씩 살펴볼게요.
본문
짧은 설명
PowerShell 백그라운드 작업이 현재 세션과 상호작용하지 않고 명령이나 식을 백그라운드에서 어떻게 실행하는지 설명해요.
자세한 설명
PowerShell은 **작업(job)**을 통해 명령과 스크립트를 동시에(concurrently) 실행해요. 동시성을 지원하기 위해 PowerShell이 제공하는 작업 유형은 세 가지가 있어요.
- RemoteJob — 명령과 스크립트가 원격 세션에서 실행돼요. 자세한 내용은 about_Remote_Jobs를 봐요.
- BackgroundJob — 명령과 스크립트가 로컬 컴퓨터의 별도 프로세스에서 실행돼요.
- PSTaskJob 또는 ThreadJob — 명령과 스크립트가 로컬 컴퓨터의 같은 프로세스 안의 별도 스레드에서 실행돼요. 자세한 내용은 about_Thread_Jobs를 봐요.
원격으로든 별도 프로세스로든, 다른 컴퓨터에서 스크립트를 실행하면 격리(isolation)가 아주 좋아져요. 원격 작업에서 어떤 오류가 나도 다른 실행 중인 작업이나 그 작업을 시작한 부모 세션에는 영향을 주지 않아요. 다만 리모팅 계층이 오버헤드를 만들긴 해요. 특히 개체 직렬화(object serialization) 때문인데, 부모 세션과 원격(작업) 세션 사이를 오가는 모든 개체는 직렬화됐다가 역직렬화돼요. 크고 복잡한 데이터 개체를 직렬화하면 계산·메모리 자원을 많이 소모하고, 네트워크로 많은 양의 데이터를 옮기게 돼요.
스레드 기반 작업은 원격·백그라운드 작업만큼 견고하지는 않아요. 같은 프로세스의 서로 다른 스레드에서 도니까요. 한 작업에서 프로세스를 망가뜨리는 치명적 오류가 나면 그 프로세스 안의 다른 작업까지 모두 종료돼요. 하지만 스레드 기반 작업은 오버헤드가 훨씬 적어요. 리모팅 계층이나 직렬화를 쓰지 않고, 결과 개체가 현재 세션의 살아 있는(live) 개체에 대한 참조로 그대로 돌아오거든요. 오버헤드가 없으니 스레드 기반 작업은 다른 작업 유형보다 더 빠르고 자원도 덜 써요.
중요 작업을 만든 부모 세션은 작업 상태를 감시하고 파이프라인 데이터를 수집해요. 작업이 완료 상태에 도달하면 부모 프로세스가 작업의 자식 프로세스를 종료해요. 만약 부모 세션이 종료되면, 실행 중인 모든 자식 작업도 그 자식 프로세스와 함께 종료돼요.
이 상황을 피하는 방법은 두 가지가 있어요.
- 작업을 **연결이 끊긴 세션(disconnected session)**에서 실행하도록 Invoke-Command로 만들기. 자세한 내용은 about_Remote_Jobs를 봐요.
- 작업 대신 새 프로세스를 만들도록 Start-Process 사용하기.
작업 cmdlet들
- Start-Job — 로컬 컴퓨터에서 백그라운드 작업을 시작해요.
- Get-Job — 현재 세션에서 시작된 백그라운드 작업을 가져와요.
- Receive-Job — 백그라운드 작업의 결과를 가져와요.
- Stop-Job — 백그라운드 작업을 중지해요.
- Wait-Job — 작업이 하나 또는 전부 완료될 때까지 명령 프롬프트를 억제해요.
- Remove-Job — 백그라운드 작업을 삭제해요.
- Invoke-Command —
AsJob매개 변수가 원격 컴퓨터에서 백그라운드 작업을 만들어요.Invoke-Command를 쓰면Start-Job을 포함해 어떤 작업 명령이든 원격으로 실행할 수 있어요.
로컬 컴퓨터에서 작업 시작하기
로컬 컴퓨터에서 백그라운드 작업을 시작하려면 Start-Job cmdlet을 써요. Start-Job 명령을 작성할 때는 작업이 실행할 명령을 **중괄호({})**로 감싸요. 그리고 ScriptBlock 매개 변수로 그 명령을 지정해요.
다음 명령은 로컬 컴퓨터에서 Get-Process 명령을 실행하는 백그라운드 작업을 시작해요.
Start-Job -ScriptBlock {Get-Process}
백그라운드 작업을 시작하면 작업이 오래 걸리더라도 명령 프롬프트는 즉시 돌아와요. 작업이 도는 동안에도 간섭 없이 세션에서 계속 작업할 수 있어요.
Start-Job 명령은 그 작업을 나타내는 개체를 반환해요. 작업 개체에는 작업에 대한 유용한 정보가 담겨 있지만, 작업 결과 자체는 들어 있지 않아요.
작업 개체를 변수에 저장해 두면 다른 작업 cmdlet들과 함께 사용해서 백그라운드 작업을 관리할 수 있어요. 다음 명령은 작업을 시작하고 결과 작업 개체를 $job 변수에 저장해요.
$job = Start-Job -ScriptBlock {Get-Process}
PowerShell 6.0부터는 파이프라인 끝에 **백그라운드 연산자(&)**를 붙여서 백그라운드 작업을 시작할 수도 있어요. 자세한 내용은 background operator를 봐요.
백그라운드 연산자를 쓰는 것은 앞 예시의 Start-Job cmdlet을 쓰는 것과 기능적으로 동일해요.
$job = Get-Process &
작업 개체 가져오기
Get-Job cmdlet은 현재 세션에서 시작된 백그라운드 작업을 나타내는 개체를 반환해요. 매개 변수 없이 쓰면 현재 세션에서 시작된 모든 작업을 반환해요.
Get-Job
작업 개체에는 작업이 끝났는지 알려 주는 State(상태) 속성이 있어요. 완료된 작업의 상태는 Complete 또는 Failed예요. 작업이 Blocked 또는 Running 상태일 수도 있어요.
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Complete True localhost Get-Process
작업 개체를 변수에 저장해 두면 나중에 그 변수로 작업을 나타낼 수 있어요. 다음 명령은 ID가 1인 작업을 가져와 $job 변수에 저장해요.
$job = Get-Job -Id 1
작업 결과 가져오기
백그라운드 작업을 실행하면 결과가 바로 나타나지는 않아요. 백그라운드 작업의 결과를 가져오려면 Receive-Job cmdlet을 써요.
다음 예시에서 Receive-Job cmdlet은 $job 변수에 저장된 작업 개체로 작업의 결과를 가져와요.
Receive-Job -Job $job
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
103 4 11328 9692 56 1176 audiodg
804 14 12228 14108 100 101.74 1740 CcmExec
668 7 2672 6168 104 32.26 488 csrss
...
작업 결과를 변수에 저장할 수도 있어요. 다음 명령은 $job 변수에 있는 작업의 결과를 $results 변수에 저장해요.
$results = Receive-Job -Job $job
부분 작업 결과 가져오고 유지하기
Receive-Job cmdlet은 백그라운드 작업의 결과를 가져와요. 작업이 완료됐으면 모든 결과를, 아직 실행 중이면 지금까지 생성된 결과를 가져와요. Receive-Job 명령을 다시 실행하면 남은 결과를 가져올 수 있어요.
기본적으로 Receive-Job은 작업 결과가 저장되는 캐시에서 결과를 삭제해요. 다시 Receive-Job을 실행하면 첫 실행 이후 새로 도착한 결과만 얻게 돼요.
다음 명령들은 작업이 완료되기 전에 실행한 Receive-Job 명령의 결과를 보여줘요.
C:\PS> Receive-Job -Job $job
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
103 4 11328 9692 56 1176 audiodg
804 14 12228 14108 100 101.74 1740 CcmExec
C:\PS> Receive-Job -Job $job
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
68 3 2632 664 29 0.36 1388 ccmsetup
749 22 21468 19940 203 122.13 3644 communicator
905 7 2980 2628 34 197.97 424 csrss
1121 25 28408 32940 174 430.14 3048 explorer
Receive-Job이 반환한 작업 결과를 삭제하지 않게 하려면 Keep 매개 변수를 써요. 다음 명령들은 아직 완료되지 않은 작업에 Keep 매개 변수를 썼을 때 어떤 효과가 있는지 보여줘요.
C:\PS> Receive-Job -Job $job -Keep
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
103 4 11328 9692 56 1176 audiodg
804 14 12228 14108 100 101.74 1740 CcmExec
C:\PS> Receive-Job -Job $job -Keep
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
103 4 11328 9692 56 1176 audiodg
804 14 12228 14108 100 101.74 1740 CcmExec
68 3 2632 664 29 0.36 1388 ccmsetup
749 22 21468 19940 203 122.13 3644 communicator
905 7 2980 2628 34 197.97 424 csrss
1121 25 28408 32940 174 430.14 3048 explorer
결과 기다리기
완료에 오래 걸리는 명령을 실행할 때는 작업 개체의 속성으로 작업이 완료됐는지 판단할 수 있어요. 다음 명령은 Get-Job으로 현재 세션의 모든 백그라운드 작업을 가져와요.
Get-Job
결과가 표로 나타나는데, 작업의 상태는 State 열에서 확인할 수 있어요.
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Complete True localhost Get-Process
2 Job2 BackgroundJob Running True localhost Get-EventLog -Log ...
3 Job3 BackgroundJob Complete True localhost dir -Path C:\* -Re...
이 경우 State 속성을 보면 Job 2가 아직 실행 중인 걸 알 수 있어요. 지금 Receive-Job으로 결과를 가져오면 불완전한 결과만 얻을 텐데요, Receive-Job을 반복해서 실행하면 모든 결과를 가져올 수 있어요. 작업이 언제 완료되는지는 State 속성으로 판단하세요.
Receive-Job -Wait 명령도 쓸 수 있어요. 이 매개 변수를 쓰면 작업이 완료되고 모든 결과가 준비될 때까지 cmdlet이 명령 프롬프트를 돌려주지 않아요.
Wait-Job cmdlet을 써서 작업의 일부 또는 전체 결과를 기다릴 수도 있어요. Wait-Job은 특정 작업 하나 또는 여러 개, 아니면 모든 작업을 기다리게 해 줘요. 다음 명령은 ID가 10인 작업을 Wait-Job으로 기다려요.
Wait-Job -Id 10
그 결과 작업이 완료될 때까지 PowerShell 프롬프트가 억제돼요. 미리 정해둔 시간만 기다릴 수도 있어요. 다음 명령은 Timeout 매개 변수로 대기를 120초로 제한해요. 시간이 지나면 명령 프롬프트가 돌아오지만, 작업은 백그라운드에서 계속 실행돼요.
Wait-Job -Id 10 -Timeout 120
작업 중지하기
백그라운드 작업을 중지하려면 Stop-Job cmdlet을 써요. 다음 명령은 System 이벤트 로그의 모든 항목을 가져오는 작업을 시작하고, 작업 개체를 $job 변수에 저장해요.
$job = Start-Job -ScriptBlock {Get-EventLog -Log System}
다음 명령은 그 작업을 중지해요. 파이프라인 연산자(|)로 $job 변수의 작업을 Stop-Job으로 보내요.
$job | Stop-Job
작업 삭제하기
백그라운드 작업을 삭제하려면 Remove-Job cmdlet을 써요. 다음 명령은 $job 변수의 작업을 삭제해요.
Remove-Job -Job $job
실패한 작업 조사하기
작업은 여러 이유로 실패할 수 있어요. 작업 개체에는 실패 원인에 대한 정보를 담은 Reason 속성이 있어요.
다음 예시는 필요한 자격 증명 없이 작업을 시작해요.
$job = Start-Job -ScriptBlock {New-Item -Path HKLM:\Software\MyCompany}
Get-Job $job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Failed False localhost New-Item -Path HKLM:...
Reason 속성을 검사해서 작업을 실패하게 만든 오류를 찾아보세요.
$job.ChildJobs[0].JobStateInfo.Reason
이 경우 원격 컴퓨터가 명령을 실행하려면 명시적 자격 증명이 필요해서 작업이 실패했어요. Reason 속성에는 다음과 같은 메시지가 들어 있어요.
Connecting to remote server failed with the following error message: "Access is denied".