다른 여러 Shell들과 마찬가지로 Powershell 역시 파이프라인을 지원 한다. 여기서 파이프라인이란 명령들이 이어 지면서 앞 명령의 출력이 뒤 명령의 입력이 되는 방식을 말한다.
명령 파이프라인을 사용하는 것과 사용하지 않고 출력을 직접(변수 등을 통해) 전달하는 방식은 큰 차이점이 있다.
파이프라인을 사용 할 때에는 앞의 명령이 완전히 끝날때(모든 출력이 종료될때) 까지 대기 하는 것이 아니라. 앞뒤의 명령이 하나의 명령처럼 연결되어 출력이 나올때 마다 각 항목이 다음 명령으로 전달 되어 개별적으로 동작 한다. 물론 최종 출력도 항목마다 즉시 확인 할 수 있다.
Text를 입 출력 하는 타 Shell들과 다르게 .Net Framework 기반인 Powershell은 모든 입 출력의 개체로 이루어진다. Text라 할 지라도 그것은 String 개체이다. 따라서 파이프라인으로 전달되는 항목역시 일반적인 Text가 아니라. 출력에 해당 되는 개체이다.
예를 들어 메모장(notepad)을 띄우고 메모장 Process를 찾기 위해 다음 명령을 입력 해 보자.
Get-Process -Name notepad
다음과 같이 (비슷하게) 결과가 보일 것이다.
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 107 9 3080 9120 112 0.02 4256 notepad
이것은 단순한 텍스트 출력이 아니라. 실제 메모장의 .Net Process 개체(System.Diagnostics.Process)가 반환된 것이다. 게다가 리턴 받은 개체의 Property를 get, set 하거나 Method도 자유롭게 호출 할 수 있다.
개체의 구조를 파악 하기 위해선 Get-Member 명령을 이용 할 수 있는데 다음과 같이 파이프 라인으로 전달 해 보자.
Get-Process -Name notepad | Get-Member
다음과 같은 결과를 얻을 것이다.
TypeName: System.Diagnostics.Process Name MemberType Definition ---- ---------- ---------- Handles AliasProperty Handles = Handlecount Name AliasProperty Name = ProcessName NPM AliasProperty NPM = NonpagedSystemMemorySize PM AliasProperty PM = PagedMemorySize VM AliasProperty VM = VirtualMemorySize WS AliasProperty WS = WorkingSet Disposed Event System.EventHandler Disposed(System.Object, ErrorDataReceived Event System.Diagnostics.DataReceivedEventHandler Exited Event System.EventHandler Exited(System.Object, S OutputDataReceived Event System.Diagnostics.DataReceivedEventHandler BeginErrorReadLine Method System.Void BeginErrorReadLine() BeginOutputReadLine Method System.Void BeginOutputReadLine() CancelErrorRead Method System.Void CancelErrorRead() CancelOutputRead Method System.Void CancelOutputRead() ...
TypeName 항목이 System.Diagnostics.Process 이고 모든 Member들을 확인 할 수 있을 것이다. Get-Member로 전달하지 않고 출력 했을 때 항목들이 AliasProperty로 지정되 있음을 주목하자.
이 개체의 Kill() Method를 호출하여 notepad를 닫아 보자.
$NotePadProc = Get-Process -Name notepad
$NotePadProc 는 이제 notepad Process개체이다. 만약 notepad 가 하나이상 있었다면 Collection(Array) 형태로 존재 할 것이다.
Pipeline과 Foreah-Object 를 이용하여 notepad Process를 모두 닫아보자.
$NotePadProc | Foreach-Object { $_.Kill() }
$_는 Collection요소 중 하나이다. 즉 $_에 수행하면 Collection의 모든 요소에 수행 한다.
만약 notepad Process가 하나만 있었다면 다음 명령으로도 충분하다.
$NotePadProc.Kill()
위 방법 말고도 Process 를 중지 시키는 Cmdlet인 Stop-Process 를 활용 할 수 있다. 이번에도 역시 Get-Process로 Process개체를 얻고 파이프라인을 이용하여 전달 해 보자.
먼저 notepad를 여러게 실행 시키고 다음을 실행 한다.
Get-Process notepad | Stop-Process
모든 notepad들이 종료 될 것이다.
여기서 주의 할 점은 Get-Process 에서 notepad항목이 하나씩 발견 될때 마다 즉시 Stop-Process로 전달되어 처리 되고 Get-Process는 계속 다음 notepad를 찾아서 Stop-Process로 전달 한다는 것이다..
다시 notepad를 여러게 실행시키고 이번엔 Pipeline 없이 수행해 보자.
$NoteProcs = Get-Process notepad Stop-Process $NoteProcs
에러가 발생 할 것이다. 이유는 Stop-Process 명령은 Process Id를 입력으로 받는데 $NoteProcs이 Process 가 아닌 Collection(Object[]) 이기 때문이다. 파이프 라인 상에서는 Process를 발견 할때마다 Stop-Process로 전달 되었지만 여기선 이미 Get-Process Cmdlet이 notepad Process들을 모두 찾아 Collection으로 반환한 상태이다.
이상태에서 제대로 처리 하기 위해선 다음과 같이 하나씩 Stop-Process로 전달 해 줘야 한다.
$NoteProcs = Get-Process notepad Foreach ($proc in $NoteProcs) { Stop-Process $proc.Id } Stop-Process $NoteProcs
혹은
$NoteProcs = Get-Process notepad $NoteProcs | Foreach-Object { Stop-Process $_.Id }
이와 같이 파이프라인을 잘 활용하면 복잡한 Command를 간결하게 작성 할 수 있고 여러 과정이 동시에 일어 나기 때문에 상황에 따라 더 좋은 퍼포먼스를 기대 할 수도 있다. 게다가 개별적으로 즉시 처리 되기 때문에 캐쉬 하는 부분이 적어 리소스를 절약 하는 효과도 기대 할 수 있다.