about_Command_Precedence — PowerShell이 같은 이름의 명령 중 어떤 걸 실행할까
about_Command_Precedence — PowerShell이 같은 이름의 명령 중 어떤 걸 실행할까
PowerShell 세션에 같은 이름을 가진 명령이 둘 이상 있으면 어떤 명령이 실행될까요? 이 문서에서는 PowerShell이 실행할 명령을 정하는 명령 우선 순위(Command Precedence) 규칙을 설명해요. 세션 안에서 같은 이름 때문에 명령이 숨겨지거나 대체되기도 하는데, 숨겨진 명령을 실행하는 방법과 이름 충돌을 피하는 방법까지 함께 다뤄볼게요.
기본 개념
명령 우선 순위는 세션에 같은 이름을 가진 명령이 여러 개 있을 때 PowerShell이 어떤 명령을 실행할지 결정하는 규칙이에요. 같은 이름 때문에 명령이 숨겨지거나(hidden) 대체되기도(replaced) 하는데, 이 문서를 통해 숨겨진 명령을 실행하는 방법과 이름 충돌을 피하는 방법을 배울 수 있어요.
명령 우선 순위 (Command precedence)
같은 이름을 가진 명령이 세션에 둘 이상 있으면, PowerShell은 다음 규칙에 따라 실행할 명령을 골라요.
명령에 경로를 지정하면 PowerShell은 그 경로에 있는 명령을 실행해요. 예를 들어 아래 명령은 C:\TechDocs 디렉터리에 있는 FindDocs.ps1 스크립트를 실행해요.
C:\TechDocs\FindDocs.ps1
실행 가능한 명령은 전체 경로로 실행할 수 있어요. 보안 기능으로서, PowerShell은 $Env:PATH 환경 변수에 나열된 경로에 있는 명령이 아니라면 실행 가능한 명령은 물론 PowerShell 스크립트와 네이티브 명령도 실행하지 않아요.
현재 디렉터리에 있는 실행 파일을 실행하려면 전체 경로를 지정하거나 상대 경로 .\\로 현재 디렉터리를 나타내면 돼요. 예를 들어 현재 디렉터리의 FindDocs.ps1 파일을 실행하려면 이렇게 입력해요.
.\FindDocs.ps1
경로를 지정하지 않으면 PowerShell은 다음 순서대로 실행할 명령을 찾아요.
- Alias
- Function
- Cmdlet (자세한 내용은 Cmdlet name resolution 참조)
- 외부 실행 파일 (PowerShell 스크립트 파일 포함)
그래서 help를 입력하면 PowerShell은 먼저 help라는 alias를 찾고, 다음으로 help라는 함수, 마지막으로 help라는 cmdlet을 찾아요. 그리고 가장 먼저 찾은 help 항목을 실행해요. 예를 들어 세션에 Get-Map이라는 cmdlet과 함수가 모두 있다면, Get-Map을 입력했을 때 PowerShell은 함수를 실행해요.
참고: 이 규칙은 이미 로드된 명령에만 적용돼요.
build라는 실행 파일이 있고, 현재 세션에 로드되지 않은 모듈 안에Invoke-Build라는 이름의 함수를 가리키는buildalias가 있으면, PowerShell은build실행 파일을 실행해요. 외부 실행 파일을 찾으면 모듈을 자동 로드하지 않아요. 외부 실행 파일이 없을 때만 해당 이름의 alias, 함수, cmdlet이 호출돼요.
같은 이름의 항목 해결하기 (Resolve items with the same names)
이런 규칙 때문에 같은 이름의 항목이 대체되거나 숨겨질 수 있어요.
모듈 이름으로 항목 이름을 한정하는 것처럼 원래 항목에 여전히 접근할 수 있으면 그 항목은 숨겨졌거나(hidden) 가려진(shadowed) 거예요. 예를 들어 세션에 있는 cmdlet과 같은 이름의 함수를 가져오면(import) cmdlet은 대체되지 않고 숨겨져요. 이때는 모듈 한정 이름(module-qualified name)으로 cmdlet을 실행할 수 있어요.
항목이 대체되거나 덮어써지면(replaced/overwritten) 원래 항목에 더 이상 접근할 수 없어요. 예를 들어 세션에 있는 변수와 같은 이름의 변수를 가져오면 원래 변수는 대체돼요. 변수는 모듈 이름으로 한정할 수 없어요.
명령줄에서 함수를 만든 뒤 같은 이름의 함수를 가져오면 원래 함수는 대체돼요.
숨겨진 명령 찾기 (Find hidden commands)
Get-Command cmdlet의 All 매개 변수는 숨겨졌거나 대체된 명령이어도 지정된 이름의 모든 명령을 가져와요. PowerShell 3.0부터 기본적으로 Get-Command는 명령 이름을 입력했을 때 실제로 실행되는 명령만 가져와요.
아래 예시에서 세션에는 Get-Date 함수와 Get-Date cmdlet이 있어요. Get-Command로 어떤 명령이 먼저 선택되는지 확인할 수 있어요.
Get-Command Get-Date
CommandType Name ModuleName
----------- ---- ----------
Function Get-Date
All 매개 변수를 사용하면 실행 가능한 Get-Date 명령을 모두 나열해요.
Get-Command Get-Date -All
CommandType Name Version Source
----------- ---- ------- ------
Function Get-Date
Cmdlet Get-Date 7.0.0.0 Microsoft.PowerShell.Utility
Get-Command where -All
CommandType Name Version Source
----------- ---- ------- ------
Alias where -> Where-Object
Application where.exe 10.0.22621.1 C:\Windows\system32\where.exe
같은 이름의 다른 명령과 구별되는 한정 정보를 포함해 특정 명령을 실행할 수도 있어요. cmdlet은 모듈 한정 이름을, 실행 파일은 파일 확장자를 포함할 수 있어요. 예를 들어 where의 실행 파일 버전을 실행하려면 where.exe를 사용해요.
모듈 한정 이름 사용하기 (Use module-qualified names)
cmdlet의 모듈 한정 이름을 사용하면 같은 이름의 항목에 숨겨진 명령도 실행할 수 있어요. 예를 들어 Get-Date cmdlet을 모듈 이름 Microsoft.PowerShell.Utility나 그 경로로 한정해 실행할 수 있어요. 모듈 한정 이름을 사용하면 $PSModuleAutoLoadingPreference 값에 따라 모듈이 세션에 자동으로 가져와질 수도 있어요.
참고: 변수나 alias는 모듈 이름으로 한정할 수 없어요.
모듈 한정 이름을 사용하면 의도한 명령을 확실히 실행할 수 있어요. 배포할 스크립트를 작성할 때 cmdlet을 호출하는 권장 방법이에요.
아래 예시는 모듈 이름으로 명령을 한정하는 방법을 보여줘요.
중요: 모듈 한정은 플랫폼과 관계없이 백슬래시 문자(
\)로 모듈 이름과 명령 이름을 구분해요.
New-Alias -Name "Get-Date" -Value "Get-ChildItem"
Microsoft.PowerShell.Utility\Get-Date
Tuesday, May 16, 2023 1:32:51 PM
MapFunctions 모듈의 New-Map 명령을 실행하려면 모듈 한정 이름을 사용해요.
MapFunctions\New-Map
명령이 어느 모듈에서 가져와졌는지 찾으려면 명령의 ModuleName 속성을 사용해요.
(Get-Command <command-name>).ModuleName
예를 들어 Get-Date cmdlet의 출처를 찾으려면 이렇게 입력해요.
(Get-Command Get-Date).ModuleName
Microsoft.PowerShell.Utility
모듈 경로로 명령 이름을 한정하려면 경로 구분자로 **슬래시(/)**를, 명령 이름 앞에는 **백슬래시(\)**를 사용해야 해요. 아래 예시로 Get-Date cmdlet을 실행해요.
//localhost/c$/Progra~1/PowerShell/7-preview/Modules/Microsoft.PowerShell.Utility\Get-Date
경로는 전체 경로일 수도, 현재 위치에 대한 상대 경로일 수도 있어요. Windows에서는 드라이브 한정 경로(drive-qualified path)를 사용할 수 없어요. 위 예시처럼 UNC 경로나 현재 드라이브에 대한 상대 경로를 사용해야 해요. 아래 예시는 현재 위치가 C: 드라이브에 있다고 가정해요.
/Progra~1/PowerShell/7-preview/Modules/Microsoft.PowerShell.Utility\Get-Date
호출 연산자 사용하기 (Use the call operator)
호출 연산자(&)를 Get-ChildItem(alias는 dir), Get-Command, Get-Module 호출과 함께 사용해 숨겨진 명령을 실행할 수도 있어요.
호출 연산자는 하위 범위(child scope)에서 문자열과 스크립트 블록을 실행해요. 자세한 내용은 about_Operators를 참조하세요.
예를 들어 Map이라는 alias에 숨겨진 Map이라는 함수를 실행하려면 아래 명령을 사용해요.
& (Get-Command -Name Map -CommandType Function)
또는
& (dir Function:\map)
숨겨진 명령을 변수에 저장해 실행하기 쉽게 만들 수도 있어요. 예를 들어 아래 명령은 Map 함수를 $myMap 변수에 저장한 뒤 호출 연산자로 실행해요.
$myMap = (Get-Command -Name map -CommandType Function)
& ($myMap)
대체된 항목 (Replaced items)
대체된 항목은 더 이상 접근할 수 없는 항목이에요. 모듈에서 같은 이름의 항목을 가져와 항목을 대체할 수 있어요. 예를 들어 세션에 Get-Map 함수를 입력하고, Get-Map이라는 함수를 가져오면 원래 함수는 대체돼요. 현재 세션에서 다시 꺼낼 수 없어요.
변수와 alias는 호출 연산자나 한정 이름으로 실행할 수 없기 때문에 숨길 수 없어요. 모듈에서 변수와 alias를 가져오면 세션에서 같은 이름의 변수와 alias를 대체해요.
Cmdlet 이름 해석 (Cmdlet name resolution)
cmdlet의 한정 이름을 사용하지 않으면 PowerShell은 해당 cmdlet이 현재 세션에 로드되어 있는지 확인해요. 같은 cmdlet 이름을 포함한 모듈이 여러 개 로드되어 있으면 PowerShell은 알파벳순으로 가장 먼저 찾은 모듈의 cmdlet을 사용해요.
cmdlet이 로드되어 있지 않으면 PowerShell은 설치된 모듈을 검색해 그 cmdlet을 포함한 첫 번째 모듈을 자동 로드하고 해당 cmdlet을 실행해요. PowerShell은 $Env:PSModulePath 환경 변수에 정의된 각 경로에서 모듈을 검색해요. 경로는 변수에 나열된 순서대로 검색되고, 각 경로 안에서는 모듈을 알파벳순으로 검색해요. PowerShell은 가장 먼저 찾은 일치 항목의 cmdlet을 사용해요.
이름 충돌 피하기 (Avoid name conflicts)
명령 이름 충돌을 관리하는 가장 좋은 방법은 충돌을 애초에 막는 것이에요. 명령 이름을 지을 때 고유한 이름을 사용하세요. 예를 들어 명령의 명사(noun)에 이니셜이나 회사 이름 약어를 추가해요.
PowerShell 모듈이나 다른 세션에서 명령을 가져올 때는 Import-Module 또는 Import-PSSession cmdlet의 Prefix 매개 변수로 명령 이름의 명사에 접두사를 붙일 수 있어요.
예를 들어 아래 명령은 DateFunctions 모듈을 가져올 때 PowerShell과 함께 제공되는 Get-Date, Set-Date cmdlet과의 충돌을 피해요.
Import-Module -Name DateFunctions -Prefix ZZ
외부 실행 파일 실행하기 (Run external executables)
Windows에서 PowerShell은 $Env:PATHEXT 환경 변수에 나열된 파일 확장자를 실행 파일로 취급해요. Windows 실행 파일이 아닌 파일은 Windows에 넘겨 처리하게 해요. Windows는 파일 연결(file association)을 찾아 해당 확장자의 기본 Windows Shell 동사를 실행해요. 확장자별 실행을 지원하려면 그 연결이 시스템에 등록되어 있어야 해요.
파일 확장자의 실행 엔진은 CMD 명령 셸의 ftype과 assoc 명령으로 등록할 수 있어요. PowerShell에는 파일 처리기를 등록하는 직접적인 방법이 없어요. 자세한 내용은 ftype 명령 문서를 참조하세요.
현재 세션에서 파일 확장자를 실행 파일로 인식하게 하려면 그 확장자를 $Env:PATHEXT 환경 변수에 추가해야 해요.