about_Foreach
about_Foreach
PowerShell의 foreach 문은 컬렉션(collection)에 담긴 값들을 하나씩 차례대로 꺼내서 처리하게 해 주는 언어 구조예요. 배열처럼 여러 개의 항목을 담고 있는 대상이라면 어떤 것이든 반복해서 훑는 데 쓸 수 있어요. 반복할 때마다 달라지는 값에 이름을 붙여주면 그 값을 활용해서 작업을 실행할 수 있답니다.
출처: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_foreach
본문
짧게 설명하면
컬렉션에 담긴 모든 항목을 차례로 지나다니면서 처리할 수 있게 해 주는 언어 명령이에요.
자세한 설명
foreach 문은 컬렉션에 있는 값의 집합을 반복해서 순회(iterate)하기 위한 언어 구조예요.
가장 단순하고 가장 흔하게 쓰는 컬렉션은 배열(array)이에요. foreach 루프 안에서 배열에 들어 있는 각 항목에 대해 명령을 하나 또는 여러 개 실행하는 일이 아주 흔하죠.
구문(Syntax)
foreach 문의 구문은 다음과 같아요.
foreach ($<item> in $<collection>){<statement list>}
괄호 안에 있는 foreach 문의 부분은 반복할 변수와 컬렉션을 나타내요. PowerShell은 foreach 루프가 실행될 때 $<item> 변수를 자동으로 만들어 줘요. 각 반복이 시작될 때 foreach는 집합 변수(collection)에서 다음 값을 꺼내 item 변수에 넣어요. {<statement list>} 블록에는 각 반복마다 실행할 명령들이 들어 있어요.
예시(Examples)
예를 들어, 아래 예시의 foreach 루프는 $letterArray 배열에 있는 값들을 화면에 보여줘요.
$letterArray = 'a','b','c','d'
foreach ($letter in $letterArray)
{
Write-Host $letter
}
이 예시에서 $letterArray는 문자열 값 a, b, c, d를 담고 있어요. foreach 문이 처음 실행되면 $letter 변수를 $letterArray의 첫 번째 항목(a)으로 설정한 다음, Write-Host를 사용해서 그 값을 화면에 보여줘요. 루프를 다시 돌면 $letter는 b로 설정되고요. 이런 패턴이 배열의 각 항목에 대해 반복돼요.
컬렉션을 반환하는 cmdlet과 함께 foreach 문을 사용할 수도 있어요. 다음 예시에서는 Get-ChildItem cmdlet이 반환한 항목 목록을 foreach 문이 차례대로 훑어요.
foreach ($file in Get-ChildItem)
{
Write-Host $file
}
if 문을 사용해서 반환되는 결과를 제한하면 이 예시를 더 정교하게 다듬을 수 있어요. 다음 예시에서 if 문은 100킬로바이트(KB)보다 큰 파일로 결과를 제한해요.
foreach ($file in Get-ChildItem)
{
if ($file.Length -gt 100KB)
{
Write-Host $file
}
}
이 예시에서 foreach 루프는 $file 변수의 속성을 사용해서 비교 연산($file.Length -gt 100KB)을 수행해요. $file 변수에는 Get-ChildItem이 반환한 개체의 모든 속성이 담겨 있어요.
다음 예시에서는 스크립트가 문 목록(statement list) 안에서 길이와 마지막 액세스 시간을 화면에 보여줘요.
foreach ($file in Get-ChildItem)
{
if ($file.Length -gt 100KB)
{
Write-Host $file
Write-Host $file.Length
Write-Host $file.LastAccessTime
}
}
foreach 루프 바깥에 있는 변수도 사용할 수 있어요. 다음 예시는 크기가 100KB를 초과하는 파일의 개수를 세요.
$i = 0
foreach ($file in Get-ChildItem) {
if ($file.Length -gt 100KB) {
Write-Host $file 'file size:' ($file.Length / 1024).ToString('F0') KB
$i = $i + 1
}
}
if ($i -ne 0) {
Write-Host
Write-Host $i ' file(s) over 100KB in the current directory.'
}
else {
Write-Host 'No files greater than 100KB in the current directory.'
}
위 예시에서 $i는 루프 바깥에서 값 0으로 시작해요. 그다음 루프 안에서 100KB보다 큰 파일이 있을 때마다 $i가 1씩 증가해요. 루프가 끝나면 if 문이 $i의 값을 평가해서 100KB가 넘는 파일의 개수를 보여줘요.
이전 예시는 파일 길이 결과를 포맷하는 방법도 보여줘요.
($file.Length / 1024).ToString('F0')
이 값은 1,024로 나눠서 바이트가 아니라 킬로바이트 단위로 결과를 보여주고, 그다음 고정 소수점(fixed-point) 형식 지정자로 포맷해서 결과에서 소수 값은 모두 제거해요. 형식 지정자의 0은 소수 자리가 표시되지 않게 해 줘요.
다음 함수는 PowerShell 스크립트와 스크립트 모듈을 파싱해서 그 안에 들어 있는 함수의 위치를 반환해요. 이 예시는 foreach 문 블록 안에서 $foreach 변수 안의 MoveNext 메서드(메서드)와 Current 속성을 어떻게 사용하는지 보여줘요.
더 자세한 내용은 Using Enumerators를 참고해요.
function Get-FunctionPosition {
[CmdletBinding()]
[OutputType('FunctionPosition')]
param(
[Parameter(Position = 0, Mandatory,
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath')]
[System.String[]]
$Path
)
process {
try {
$filesToProcess = if ($_ -is [System.IO.FileSystemInfo]) {
$_
} else {
Get-Item -Path $Path
}
$parser = [System.Management.Automation.Language.Parser]
foreach ($item in $filesToProcess) {
if ($item.PSIsContainer -or
$item.Extension -notin @('.ps1', '.psm1')) {
continue
}
$tokens = $errors = $null
$ast = $parser::ParseFile($item.FullName, ([ref]$tokens),
([ref]$errors))
if ($errors) {
$msg = "File '{0}' has {1} parser errors." -f $item.FullName,
$errors.Count
Write-Warning $msg
}
:tokenLoop foreach ($token in $tokens) {
if ($token.Kind -ne 'Function') {
continue
}
$position = $token.Extent.StartLineNumber
do {
if (-not $foreach.MoveNext()) {
break tokenLoop
}
$token = $foreach.Current
} until ($token.Kind -in @('Generic', 'Identifier'))
$functionPosition = [pscustomobject]@{
Name = $token.Text
LineNumber = $position
Path = $item.FullName
}
$addMemberSplat = @{
InputObject = $functionPosition
TypeName = 'FunctionPosition'
PassThru = $true
}
Add-Member @addMemberSplat
}
}
}
catch {
throw
}
}
}