명령은 네 가지다
콘솔에 이름을 치면 PowerShell은 네 종류 중에서 찾는다.
| 종류 | 무엇인가 | 예 |
|---|---|---|
| Cmdlet | .NET으로 만들어 컴파일한 명령 | Get-ChildItem |
| Function | PowerShell로 쓴 명령 | prompt, 내가 만든 함수 |
| Alias | 다른 명령의 짧은 이름 | gci → Get-ChildItem |
| Application | 운영체제의 실행 파일 | git, ssh, ping |
쓰는 쪽에서는 cmdlet과 함수를 구별할 일이 거의 없다. 둘 다 동사-명사 이름에 -매개변수를 받고 개체를 돌려준다. 어떤 종류인지는 Get-Command가 알려 준다.
Get-Command Get-ChildItem, prompt, gci, git
같은 이름이 여럿이면 별칭, 함수, cmdlet, 실행 파일 순으로 먼저 잡힌다. 별칭이 실행 파일을 가리는 일이 그래서 생긴다.
동사는 정해져 있다
이름의 앞쪽 동사는 아무 말이나 쓰지 않고 정해진 목록에서 고른다. 처음 보는 명령도 동사만 보면 무슨 일을 할지 짐작되는 것은 이 규칙 덕분이다.
Get-Verb | Measure-Object # 승인된 동사의 수
Get-Verb Resize
Verb AliasPrefix Group Description
---- ----------- ----- -----------
Resize rz Common Changes the size of a resource
2010년 글에서 이미지 크기를 바꾸는 함수를 Resize-Image로 짓고 싶었는데 Resize가 목록에 없어 Reset으로 지었다고 적었다. 지금은 Resize가 승인된 동사다.
별칭은 콘솔에서만
별칭은 손으로 칠 때 편하다.
gci -r -fo | ? Name -eq 'node_modules' | % FullName
같은 일을 스크립트에 쓸 때는 풀어 쓴다.
Get-ChildItem -Recurse -Force |
Where-Object Name -EQ 'node_modules' |
ForEach-Object FullName
이유는 둘이다. 여섯 달 뒤의 나와 동료가 읽을 수 있어야 한다. 그리고 별칭은 운영체제마다 다르다. Windows에서 ls, cat, rm, cp는 PowerShell cmdlet의 별칭이지만, 리눅스와 macOS의 PowerShell에는 그 별칭이 없어서 운영체제의 진짜 ls가 실행된다. 돌려주는 것이 개체가 아니라 글자이므로 뒤의 파이프라인이 깨진다.
curl과 wget도 같은 함정이었다. Windows PowerShell 5.1에서는 둘 다 Invoke-WebRequest의 별칭이었고, PowerShell 7에서는 어느 운영체제에서나 별칭이 아니다. curl을 치면 진짜 curl이 실행된다(Windows에도 curl.exe가 들어 있다). wget은 Windows에 없으므로 따로 깔지 않았다면 없는 명령이라고 나온다.
모듈: 명령의 묶음
명령은 모듈 단위로 묶여 있다. 설치된 모듈과 그 안의 명령은 이렇게 본다.
Get-Module -ListAvailable
Get-Command -Module Microsoft.PowerShell.Archive
모듈을 미리 불러올 필요는 없다. 그 안의 명령을 처음 쓰는 순간 자동으로 불려 온다.
새 모듈은 PowerShell 갤러리에서 받는다. 7.4부터 PSResourceGet이 들어 있어 따로 준비할 것이 없다. 예전의 Install-Module(PowerShellGet)도 남아 있지만 새 명령이 더 빠르다.
Find-PSResource -Name '*SecretManagement*'
Install-PSResource Microsoft.PowerShell.SecretManagement
Get-InstalledPSResource
Update-PSResource
갤러리는 누구나 올릴 수 있는 곳이다. 설치하기 전에 올린 사람과 내려받은 횟수를 확인한다.
Windows 전용 모듈 가운데 일부는 5.1에서만 돈다. 7에서 그런 모듈을 불러오면 뒤에서 5.1 프로세스를 띄워 대신 실행하는 호환 계층이 동작한다. 대부분은 그대로 되지만 돌아오는 것이 진짜 개체가 아니라 복사본이라 메서드를 부를 수 없다.
외부 프로그램을 부를 때
git, docker, kubectl 같은 실행 파일은 그냥 이름으로 부른다. cmdlet과 다른 점 셋을 알아 두면 덜 헤맨다.
결과는 글자다. 줄마다 문자열 하나로 들어온다. JSON으로 출력하는 옵션이 있으면 그것을 써서 개체로 바꾼다.
docker ps --format json | ConvertFrom-Json | Where-Object State -EQ 'running'
성공 여부는 종료 코드로 본다. 예외가 나지 않으므로 try로 잡히지 않는다.
git pull
if ($LASTEXITCODE -ne 0) { throw "git pull failed ($LASTEXITCODE)" }
매번 검사하기 번거로우면, 종료 코드가 0이 아닐 때 PowerShell 오류로 다루게 할 수 있다(7.4부터).
$PSNativeCommandUseErrorActionPreference = $true
$ErrorActionPreference = 'Stop'
git pull # 실패하면 여기서 멈춘다
&&와 ||로 이어 쓸 수도 있다. 앞의 것이 성공해야 뒤의 것이 실행된다.
npm test && npm publish
입력 리다이렉션 <는 없다. cmd에서 되던 이런 명령은 PowerShell에서 구문 오류다.
svnadmin load C:\svn\test < .\test.dump
2010년에는 이것 때문에 명령을 임시 .bat 파일에 적어 실행하는 Invoke-BatchCommand라는 함수를 만들어 썼다. 지금은 그럴 필요가 없다. 글자 파일이면 파이프로 넣는다.
Get-Content ./input.txt | sort
덤프처럼 바이트 그대로 넘겨야 하는 파일이면 그 한 줄만 운영체제의 셸에 맡긴다.
cmd /c 'svnadmin load C:\svn\test < .\test.dump' # Windows
sh -c 'svnadmin load /srv/svn/test < ./test.dump' # 리눅스, macOS
다음 글: 개체와 파이프라인