about_Redirection — PowerShell 출력 리디렉션 정리

about_Redirection — PowerShell 출력 리디렉션 정리

PowerShell이 만든 출력을 어디로 보낼지는 기본값이 정해져 있어요. 그런데 실제로 일을 하다 보면 "이 결과를 파일로도 남겨두고 싶다"거나 "오류만 따로 뽑아보고 싶다"는 순간이 꼭 오죠. 그럴 때 쓸 수 있는 도구가 바로 리디렉션(redirection)이에요. 이 문서에서 PowerShell의 출력 리디렉션을 한번 정리해 볼게요.

출처: Microsoft Learn — about_Redirection

본문

간단한 설명

PowerShell에서 출력을 텍스트 파일로 리디렉션하는 방법을 설명해요.

자세한 설명

기본적으로 PowerShell은 출력을 PowerShell 호스트(host)로 보내요. 보통은 콘솔 애플리케이션이죠. 그런데 이 출력을 텍스트 파일로 돌릴 수도 있고, 오류 출력을 일반 출력 스트림으로 돌릴 수도 있어요.

출력을 리디렉션하는 방법은 크게 세 가지가 있어요.

  • Out-File cmdlet — 명령 출력을 텍스트 파일로 보내요. Encoding, Force, Width, NoClobber 같은 매개 변수를 써야 할 때 주로 Out-File을 사용해요.
  • Tee-Object cmdlet — 명령 출력을 텍스트 파일로 보내면서 동시에 파이프라인으로도 계속 보내요.
  • PowerShell 리디렉션 연산자 — 리디렉션 연산자 >로 PowerShell 명령(cmdlet, 함수, 스크립트)의 출력을 보내는 건, 추가 매개 변수 없이 Out-File로 파이프하는 것과 기능적으로 같아요. PowerShell 7.4에서 네이티브 명령의 stdout 스트림을 리디렉션할 때 연산자의 동작이 바뀌었어요.

스트림에 대한 자세한 내용은 about_Output_Streams 문서를 참고해요.

리디렉션할 수 있는 출력 스트림

PowerShell은 아래 출력 스트림들의 리디렉션을 지원해요.

스트림 번호 설명 도입된 버전 쓰기 cmdlet
1 Success Stream PowerShell 2.0 Write-Output
2 Error Stream PowerShell 2.0 Write-Error
3 Warning Stream PowerShell 3.0 Write-Warning
4 Verbose Stream PowerShell 3.0 Write-Verbose
5 Debug Stream PowerShell 3.0 Write-Debug
6 Information Stream PowerShell 5.0 Write-Information, Write-Host
* All Streams PowerShell 3.0

PowerShell에는 Progress 스트림도 있는데, 이건 리디렉션을 지원하지 않아요.

중요 Success 스트림과 Error 스트림은 다른 셸에서 말하는 stdout, stderr와 비슷해요. 다만 PowerShell에서는 stdin이 입력용 파이프라인에 연결되지 않는다는 점을 기억해 두세요.

PowerShell 리디렉션 연산자

PowerShell의 리디렉션 연산자는 아래와 같아요. 여기서 n은 스트림 번호를 뜻하고, 스트림을 지정하지 않으면 Success 스트림(1)이 기본값이에요.

연산자 설명 구문
> 지정한 스트림을 파일로 보내요. n>
>> 지정한 스트림을 파일에 추가해요. n>>
>&1 지정한 스트림을 Success 스트림으로 보내요. n>&1

참고 일부 Unix 셸과 달리, PowerShell에서는 다른 스트림을 Success 스트림으로만 리디렉션할 수 있어요.

네이티브 명령의 출력 리디렉션

PowerShell 7.4부터 네이티브 명령의 stdout 스트림을 리디렉션할 때 연산자 동작이 달라졌어요. 네이티브 명령의 출력을 리디렉션할 때 연산자가 이제 바이트 스트림 데이터를 그대로 보존해요. PowerShell이 리디렉션된 데이터를 해석하지도, 추가 포맷을 붙이지도 않아요. 자세한 내용은 예제 7을 참고해요.

예제

예제 1: 오류와 출력을 파일로 리디렉션

이 예제는 성공하는 항목과 실패하는 항목에 대해 dir을 실행해요.

dir C:\, fakepath 2>&1 > .\dir.log

2>&1로 Error 스트림을 Success 스트림으로 보내고, >로 그 결과 만들어진 Success 스트림을 dir.log 파일로 보내는 구조예요.

예제 2: 모든 Success 스트림 데이터를 파일로 보내기

이 예제는 모든 Success 스트림 데이터를 script.log 파일로 보내요.

.\script.ps1 > script.log

예제 3: Success·Warning·Error 스트림을 파일로 보내기

이 예제는 리디렉션 연산자를 조합해 원하는 결과를 만드는 방법을 보여줘요.

&{
   Write-Warning "hello"
   Write-Error "hello"
   Write-Output "hi"
} 3>&1 2>&1 > C:\Temp\redirection.log

3>&1은 Warning 스트림을 Success 스트림으로 보내요. 2>&1은 Error 스트림을 Success 스트림으로 보내는데, 이때 이미 Warning 스트림 데이터도 Success 스트림에 포함돼 있어요. 마지막 >는 그렇게 Warning과 Error 스트림을 모두 담은 Success 스트림을 C:\temp\redirection.log 파일로 보내요.

예제 4: 모든 스트림을 파일로 리디렉션

이 예제는 script.ps1이라는 스크립트의 모든 스트림 출력을 script.log 파일로 보내요.

.\script.ps1 *> script.log

예제 5: 모든 Write-Host 및 Information 스트림 데이터 억제하기

이 예제는 모든 Information 스트림 데이터를 억제해요. Information 스트림 관련 cmdlet에 대한 자세한 내용은 Write-HostWrite-Information 문서를 참고해요.

&{
   Write-Host "Hello"
   Write-Information "Hello" -InformationAction Continue
} 6> $null

예제 6: 작업 기본 설정(Action Preference)의 효과 보기

Action Preference 변수와 매개 변수는 특정 스트림에 무엇이 기록될지 바꿀 수 있어요. 이 예제의 스크립트는 $ErrorActionPreference 값이 Error 스트림에 기록되는 내용에 어떤 영향을 주는지 보여줘요.

$ErrorActionPreference = 'Continue'
$ErrorActionPreference > log.txt
Get-Item /not-here 2>&1 >> log.txt

$ErrorActionPreference = 'SilentlyContinue'
$ErrorActionPreference >> log.txt
Get-Item /not-here 2>&1 >> log.txt

$ErrorActionPreference = 'Stop'
$ErrorActionPreference >> log.txt
try {
    Get-Item /not-here 2>&1 >> log.txt
}
catch {
    "`tError caught!" >> log.txt
}
$ErrorActionPreference = 'Ignore'
$ErrorActionPreference >> log.txt
Get-Item /not-here 2>&1 >> log.txt

$ErrorActionPreference = 'Inquire'
$ErrorActionPreference >> log.txt
Get-Item /not-here 2>&1 >> log.txt

$ErrorActionPreference = 'Continue'

이 스크립트를 실행하면 $ErrorActionPreferenceInquire로 설정되어 있을 때 확인 메시지가 나와요.

PS C:\temp> .\test.ps1

Confirm
Can't find path 'C:\not-here' because it doesn't exist.
[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): H
Get-Item: C:\temp\test.ps1:23
Line |
  23 |  Get-Item /not-here 2>&1 >> log.txt
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The running command stopped because the user selected the Stop option.

로그 파일을 열어보면 아래처럼 기록돼 있어요.

PS C:\temp> Get-Content .\log.txt
Continue

Get-Item: C:\temp\test.ps1:3
Line |
   3 |  Get-Item /not-here 2>&1 >> log.txt
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'C:\not-here' because it does not exist.

SilentlyContinue
Stop
    Error caught!
Ignore
Inquire

예제 7: 네이티브 명령의 이진 데이터 리디렉션

PowerShell 7.4부터 네이티브 명령의 stdout 스트림을 파일로 리디렉션하거나, 바이트 스트림 데이터를 다른 네이티브 명령의 stdin 스트림으로 파이프할 때 PowerShell이 바이트 스트림 데이터를 그대로 보존해요.

예를 들어 네이티브 명령 curl을 사용해 이진 파일을 다운로드하고 리디렉션으로 디스크에 저장할 수 있어요.

$uri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.7/powershell-7.3.7-linux-arm64.tar.gz'

# native command redirected to a file
curl -s -L $uri > powershell.tar.gz

바이트 스트림 데이터를 다른 네이티브 명령의 stdin 스트림으로 파이프할 수도 있어요. 다음 예제는 curl로 압축된 TAR 파일을 다운로드해요. 다운로드한 파일 데이터는 tar 명령으로 스트리밍되어 아카이브 내용을 추출해요.

# native command output piped to a native command
curl -s -L $uri | tar -xzvf - -C .

PowerShell 명령의 바이트 스트림 출력을 네이티브 명령의 입력으로 파이프할 수도 있어요. 다음 예제는 Invoke-WebRequest를 사용해 앞 예제와 같은 TAR 파일을 다운로드해요.

# byte stream piped to a native command
(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

# bytes piped to a native command (all at once as byte[])
,(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

이 기능은 stderr 출력을 stdout으로 리디렉션할 때는 바이트 스트림 데이터를 지원하지 않아요. stderr와 stdout 스트림을 결합하면, 결합된 스트림은 문자열 데이터로 취급돼요.

참고 사항

데이터를 추가하지 않는 리디렉션 연산자(>n>)는 경고 없이 지정된 파일의 현재 내용을 덮어써요.

다만 파일이 읽기 전용, 숨김, 또는 시스템 파일이면 리디렉션이 실패해요. 내용을 추가하는 연산자(>>n>>)는 읽기 전용 파일에는 쓰지 않지만, 시스템 파일이나 숨김 파일에는 내용을 추가해요.

읽기 전용, 숨김, 또는 시스템 파일에 리디렉션을 강제하려면 Out-File cmdlet을 Force 매개 변수와 함께 사용하세요.

파일에 쓸 때 리디렉션 연산자는 UTF8NoBOM 인코딩을 사용해요. 파일 인코딩이 다르면 출력이 올바르게 포맷되지 않을 수 있어요. 다른 인코딩으로 파일을 쓰려면 Out-File cmdlet을 Encoding 매개 변수와 함께 사용하세요.

파일에 쓸 때의 출력 너비

Out-File이나 리디렉션 연산자 중 하나로 파일에 쓸 때, PowerShell은 실행 중인 콘솔의 너비를 기준으로 테이블 출력을 파일에 포맷해요. 예를 들어 콘솔 너비가 80으로 설정된 시스템에서 Get-ChildItem Env:\Path > path.log 같은 명령으로 테이블 출력을 로깅하면, 파일 안의 출력이 80자로 잘려요.

Name                         Value
----                         -----
Path                         C:\Program Files\PowerShell\7;C:\WINDOWS…

스크립트가 실행되는 시스템에서 콘솔 너비가 임의로 설정될 수 있다는 점을 생각해 보면, 테이블 출력을 파일로 포맷할 때 직접 지정한 너비를 사용하는 편이 좋겠죠.

Out-File cmdlet은 테이블 출력에 쓸 너비를 지정하는 Width 매개 변수를 제공해요. Out-File을 호출할 때마다 -Width 2000을 붙이는 대신, $PSDefaultParameterValues 변수를 사용해 스크립트 안의 모든 Out-File 사용에 이 값을 설정할 수 있어요. 또 리디렉션 연산자(>>>)는 사실상 Out-File의 별칭이기 때문에, 스크립트 전체의 Out-File:Width 매개 변수를 설정하면 리디렉션 연산자의 포맷 너비에도 영향이 가요. 스크립트 맨 위 근처에 아래 명령을 넣어 스크립트 전체의 Out-File:Width를 설정하세요.

$PSDefaultParameterValues['Out-File:Width'] = 2000

출력 너비를 늘리면 테이블 형식 출력을 로깅할 때 메모리 소비도 늘어나요. 테이블 형식 데이터를 파일로 많이 로깅하는데 더 작은 너비로 충분하다면, 작은 너비를 사용하세요.

Get-Service 출력 같은 경우에는 여분의 너비를 활용하기 위해, 파일로 출력하기 전에 출력을 Format-Table -AutoSize로 파이프해야 할 수 있어요.

$PSDefaultParameterValues['Out-File:Width'] = 2000
Get-Service | Format-Table -AutoSize > services.log

$PSDefaultParameterValues에 대한 자세한 내용은 about_Preference_Variables 문서를 참고해요.

비교 연산자와의 혼동 가능성

> 연산자는 Greater-than 비교 연산자(다른 프로그래밍 언어에서 흔히 >로 표기하는 그 연산자)와 혼동하면 안 돼요.

비교 대상에 따라 >를 쓴 출력이 (36이 42보다 크지 않으니) 맞아 보일 수 있어요.

PS> if (36 > 42) { "true" } else { "false" }
false

그런데 로컬 파일시스템을 확인해 보면 내용이 36인 42라는 파일이 쓰여 있다는 걸 알 수 있어요.

PS> dir

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
------          1/02/20  10:10 am              3 42

PS> cat 42
36

반대 방향인 <(less than) 비교를 시도하면 시스템 오류가 발생해요.

PS> if (36 < 42) { "true" } else { "false" }
ParserError:
Line |
   1 |  if (36 < 42) { "true" } else { "false" }
     |         ~
     | The '<' operator is reserved for future use.

숫자 비교가 필요하다면 -lt-gt를 사용해야 해요. 자세한 내용은 about_Comparison_Operators 문서의 -gt 연산자를 참고해요.

더 알아보기