Hey, Scripting Guy!서버 가동 시간 계산

Microsoft Scripting Guys

코드 다운로드 위치: HeyScriptingGuy2008_12.exe(152KB)

가동은 가동 상태를 말하고 가동 중지는 가동 중지 상태를 말합니다. 이 논리는 자명한 듯하지만, 서버 가동 시간에 관해 말할 때는 예외입니다. 가동 시간을 알려면 가동 중지 시간을 알아야 합니다. 서버 가동 중지 시간을 걱정하는 경우를 제외하고 거의 모든 네트워크 관리자는 서버 가동 시간에 민감합니다. 대부분의 관리자는 가동 시간 목표가 있으며 가동 시간 보고서를 경영진에게 제출해야 합니다.

그리 어려운 일은 아닙니다. Win32_OperatingSystem WMI 클래스를 사용하면 될 것입니다. 이 클래스의 두 속성, 즉 LastBootUpTime과 LocalDateTime을 사용하면 식은죽 먹기라고 할 수 있습니다. LocalDateTime에서 LastBootUptime을 빼기만 하면 됩니다. 그리고 나서 저녁 식사 전까지 나인홀 라운딩 한 게임까지 해치울 여유도 있습니다.

즉, Windows PowerShell을 실행하여 Win32_OperatingSystem WMI 클래스를 쿼리하고 다음과 같이 속성을 선택합니다.

PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.LocalDateTime - $wmi.LastBootUpTime

그러나 이 명령을 실행하면 서버 가동 시간에 관한 정보가 아니라 그림 1과 같은 오류 메시지가 나타날 뿐입니다.

fig01.gif

그림 1 WMI UTC 시간 값을 빼려고 하면 오류가 반환됩니다.(더 크게 보려면 이미지를 클릭하십시오.)

이 오류 메시지는 약간 오해의 소지가 있습니다. "Bad numeric constant(잘못된 숫자 상수)." 무슨 소리일까요? 숫자가 뭐고 상수가 뭔지는 다들 알고 있습니다만, 이것이 도대체 시간과 무슨 상관이 있을까요?

이상한 오류 메시지가 표시될 겨우 그 스크립트가 구문 분석하려고 하는 데이터를 직접 살펴보는 것이 상책입니다. 게다가 Windows PowerShell에서는 어떤 유형의 데이터가 사용되고 있는지 확인할 이유가 있습니다.

스크립트에서 사용하는 데이터를 검사하려면 화면에 출력하면 됩니다. 이렇게 나타날 것입니다.

PS C:\> $wmi.LocalDateTime
20080905184214.290000-240

숫자가 약간 이상하게 보입니다. 무슨 날짜가 이럴까요? 그것을 알아내기 위해 GetType 메서드를 사용합니다. GetType의 좋은 점은 거의 언제나 사용 가능하다는 것입니다. 그냥 호출하기만 하면 됩니다. 그리고 여기에 문제의 원인이 있습니다. LocalDateTime 값은 System.DateTime 값이 아니라 문자열로 보고된다는 것입니다.

PS C:\> $wmi.LocalDateTime.gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True     True     String
System.Object

어떤 시간에서 다른 시간을 빼려는 경우 문자열이 아니라 시간 값으로 처리되는지 확인하십시오. Windows PowerShell이 모든 WMI 클래스에 추가하는 ConvertToDateTime 메서드를 사용하면 간단하게 알 수 있습니다.

PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.ConvertToDateTime($wmi.LocalDateTime) –
$wmi.ConvertToDateTime($wmi.LastBootUpTime)

어떤 시간 값에서 다른 시간 값을 뺄 때 System.TimeSpan 개체의 인스턴스를 사용하게 됩니다. 따라서 많은 연산을 수행할 필요 없이 가동 시간 정보의 표시 방법을 선택할 수 있습니다. 어떤 속성을 표시할 것인지만 선택하면 됩니다. 물론 TotalMilliseconds가 아니라 TotalDays 단위로 가동 시간이 계산됩니다. System.TimeSpan 개체의 기본 표시는 다음과 같습니다.

Days              : 0
Hours             : 0
Minutes           : 40
Seconds           : 55
Milliseconds      : 914
Ticks             : 24559148010
TotalDays         : 0.0284249398263889
TotalHours        : 0.682198555833333
TotalMinutes      : 40.93191335
TotalSeconds      : 2455.914801
TotalMilliseconds : 2455914.801

이 방법의 문제점은 서버가 마지막으로 재시작된 후의 가동 기간만 알려 준다는 것입니다. 가동 중지 시간은 계산되지 않습니다. 여기서 가동은 가동 중지가 됩니다. 가동 시간을 계산하려면 가동 중지 시간을 알아야 합니다.

그럼 서버가 얼마 동안 가동되지 않았는지 어떻게 계산합니까? 서버가 시작하는 시점과 서버가 종료하는 시점을 알아야 합니다. 시스템 이벤트 로그에서 이 정보를 얻을 수 있습니다. 이벤트 로그는 서버나 워크스테이션에서 시작하는 첫 번째 프로세스 중 하나이며 또한 서버가 종료될 때 마지막으로 중지하는 프로세스 중 하나이기도 합니다. 이 시작/중지 이벤트 각각은 이벤트 ID를 생성합니다. 이벤트 로그가 시작할 때는 6005, 이벤트 로그가 중지할 때는 6006입니다. 그림 2에서는 이벤트 로그가 시작하는 예를 보여 줍니다.

fig02.gif

그림 2 컴퓨터 시작 직후 이벤트 로그 서비스가 시작합니다. (더 크게 보려면 이미지를 클릭하십시오.)

시스템 로그로부터 6005 및 6006 이벤트를 수집하고 정렬한 다음 중지에서 시작을 빼면 재시작 시점 사이에 서버가 중지했던 시간의 양을 알 수 있습니다. 그런 다음 해당 기간(분)에서 그 값을 빼면 서버 가동 시간의 비율(%)을 계산할 수 있습니다. 그림 3에서 보여 주는 CalculateSystemUpTimeFromEventLog.ps1 스크립트는 이 방법을 사용합니다.

그림 3 CalculateSystemUpTimeFromEventLog 3

#---------------------------------------------------------------
# CalculateSystemUpTimeFromEventLog.ps1
# ed wilson, msft, 9/6/2008
# Creates a system.TimeSpan object to subtract date values
# Uses a .NET Framework class, system.collections.sortedlist to sort the events from eventlog.
#---------------------------------------------------------------
#Requires -version 2.0
Param($NumberOfDays = 30, [switch]$debug)

if($debug) { $DebugPreference = " continue" }

[timespan]$uptime = New-TimeSpan -start 0 -end 0
$currentTime = get-Date
$startUpID = 6005
$shutDownID = 6006
$minutesInPeriod = (24*60)*$NumberOfDays
$startingDate = (Get-Date -Hour 00 -Minute 00 -Second 00).adddays(-$numberOfDays)

Write-debug "'$uptime $uptime" ; start-sleep -s 1
write-debug "'$currentTime $currentTime" ; start-sleep -s 1
write-debug "'$startingDate $startingDate" ; start-sleep -s 1

$events = Get-EventLog -LogName system | 
Where-Object { $_.eventID -eq  $startUpID -OR $_.eventID -eq $shutDownID `
  -and $_.TimeGenerated -ge $startingDate } 

write-debug "'$events $($events)" ; start-sleep -s 1

$sortedList = New-object system.collections.sortedlist

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated, $event.eventID )
} #end foreach event
$uptime = $currentTime - $sortedList.keys[$($sortedList.Keys.Count-1)]
Write-Debug "Current uptime $uptime"

For($item = $sortedList.Count-2 ; $item -ge 0 ; $item -- )
{ 
 Write-Debug "$item `t `t $($sortedList.GetByIndex($item)) `t `
   $($sortedList.Keys[$item])" 
 if($sortedList.GetByIndex($item) -eq $startUpID)
 {
  $uptime += ($sortedList.Keys[$item+1] - $sortedList.Keys[$item])
  Write-Debug "adding uptime. `t uptime is now: $uptime"
 } #end if  
} #end for item 

"Total up time on $env:computername since $startingDate is " + "{0:n2}" -f `
  $uptime.TotalMinutes + " minutes."
$UpTimeMinutes = $Uptime.TotalMinutes
$percentDownTime = "{0:n2}" -f (100 - ($UpTimeMinutes/$minutesInPeriod)*100)
$percentUpTime = 100 - $percentDowntime

"$percentDowntime% downtime and $percentUpTime% uptime."

이 스크립트는 Param 문을 사용하여 두 개의 명령줄 매개 변수를 정의하는 것으로 시작합니다. 이 매개 변수의 값은 명령줄에서 스크립트를 실행할 때 변경할 수 있습니다. 첫 번째 매개 변수인 $NumberOfDays에서는 가동 시간 보고서에 사용할 다른 일 수를 지정할 수 있습니다. 여기서는 스크립트에 기본값 30일을 제공했으므로 이 매개 변수의 값을 지정하지 않고 스크립트를 실행할 수 있습니다. 물론 필요한 경우 이 값을 변경할 수 있습니다.

두 번째 매개 변수인 [switch]$debug는 전환된 매개 변수로서, 스크립트 실행 시 명령줄에 이 매개 변수를 포함시키면 스크립트로부터 특정 디버깅 정보를 얻을 수 있습니다. 이 정보는 스크립트에서 얻은 결과에 대한 신뢰도를 높이는 데 기여할 수 있습니다. 때때로 6006 이벤트 로그 서비스 중지 메시지가 나타나지 않을 때도 있습니다. 아마도 서버의 치명적인 오류로 인해 이벤트 로그 쓰기가 불가능해지고 그로 인해 스크립트는 어떤 가동 시간 값을 다른 가동 시간 값에서 빼는 계산을 수행하여 잘못된 결과가 나오는 것입니다.

명령줄에서 $debug 변수가 제공되면 이 변수는 Variable: drive에 나타납니다. 여기서 $debugPreference 변수의 값은 continue로 설정되었습니다. 즉 스크립트는 계속 실행되며 Write-Debug에 제공된 모든 값이 표시됩니다. 기본적으로 $debugPreference의 값은 silentlycontinue입니다. 즉, $debugPreference의 값을 continue로 설정하지 않을 경우 스크립트가 실행되지만 Write-Debug에 제공된 값은 silent가 됩니다(즉 표시되지 않음).

스크립트가 실행될 때 그 결과 출력에서는 발생하는 6005 및 6006 이벤트 로그 항목 각각을 나열하며(그림 4 참조) 가동 시간 계산을 표시합니다. 이 정보를 사용하여 결과의 정확성을 확인할 수 있습니다.

fig04.gif

그림 4 디버그 모드는 가동 시간 계산에 추가되는 각 시간 값의 추적을 표시합니다.(더 크게 보려면 이미지를 클릭하십시오.)

다음 단계는 System.TimeSpan 개체의 인스턴스를 만드는 것입니다. New-Object cmdlet를 이용하면 날짜 차이 계산에 사용할 기본 timespan 개체를 만들 수 있습니다.

PS C:\> [timespan]$ts = New-Object system.timespan

그러나 Windows PowerShell에는 timespan 개체를 만들 수 있는 New-TimeSpan cmdlet가 있으므로, 이것을 사용하는 것이 좋습니다. 이 cmdlet를 사용하면 스크립트를 쉽게 읽을 수 있으며, 생성된 개체는 New-Object로 만든 timespan 개체와 동일합니다.

이제 몇 개의 변수를 초기화할 수 있습니다. 첫 번째로 $currentTime은 현재 시간과 날짜 값을 저장하는 데 사용됩니다. Get-Date cmdlet에서 이 정보를 얻습니다.

$currentTime = get-Date

그 다음에는 시작 및 종료 이벤트 ID 번호를 저장하는 두 변수를 초기화합니다. 이 작업을 꼭 할 필요는 없으나, 두 변수가 문자열 리터럴 값으로 포함되는 것을 방지하면 코드 읽기 및 문제 해결이 더 용이해집니다.

다음 단계에서는 해당 기간을 분 단위로 환산하는 계산의 결과를 저장할 $minutesInPeriod라는 변수를 만듭니다.

$minutesInPeriod = (24*60)*$NumberOfDays

마지막으로, $startingDate 변수를 만들어야 합니다. 이 변수는 보고 기간의 시작 시간을 나타내는 System.DateTime 개체를 저장합니다. 날짜는 해당 기간 시작일의 자정입니다.

$startingDate = (Get-Date -Hour 00 -Minute 00 -Second 00).adddays(-$numberOfDays)

변수를 만든 후 이벤트 로그에서 이벤트를 검색하고 쿼리 결과를 $events 변수에 저장합니다. Get-EventLog cmdlet를 사용하여 이벤트 로그를 쿼리합니다. 로그 이름으로 "system"을 지정합니다. Windows PowerShell 2.0에서는 –source 매개 변수를 사용하면 Where-Object cmdlet에서 정렬해야 하는 정보의 양을 줄일 수 있습니다. 그러나 Windows PowerShell 1.0에서는 그러한 선택 기회가 없으며, 따라서 쿼리에서 반환하는, 필터링되지 않은 모든 이벤트를 정렬해야 합니다. 따라서 이벤트를 Where-Object cmdlet 파이프라인으로 보내 알맞은 이벤트 로그 항목을 필터링합니다. Where-Object 필터를 살펴보면 왜 Scripting Guys에서 매개 변수를 저장할 변수를 생성하게 했는지 알 수 있습니다.

문자열 리터럴을 사용했을 때보다 훨씬 효과적으로 명령을 읽습니다. get eventIDs는 $startUpID 또는 $shutDownID와 같습니다. 또한 이벤트 로그 항목의 timeGenerated 속성이 $startingDate보다 크거나 같은지 다음과 같이 확인해야 합니다.

$events = Get-EventLog -LogName system |
Where-Object { $_.eventID -eq  $startUpID -OR $_.eventID -eq $shutDownID -and $_.TimeGenerated -ge $startingDate }

이 명령은 로컬에서만 실행됩니다. Windows Power­Shell 2.0에서는 –computerName 매개 변수를 사용하여 원격에서 명령을 작동할 수 있습니다.

다음 단계는 정렬된 목록 개체를 만드는 것입니다. 왜일까요? 이벤트의 모음을 살펴볼 때 이벤트 로그 항목이 보고된 순서가 보장되지 않기 때문입니다. Sort-Object cmdlet 파이프라인으로 개체를 보내고 그 결과를 다시 어떤 변수에 저장하더라도, 개체를 하나씩 살펴보면서 그 결과를 해시 테이블에 저장할 때 이 목록에 정렬 절차의 결과가 저장되었다고 확신할 수 없습니다.

이 까다롭고 디버깅하기 어려운 문제를 피하기 위해 System.Collections.SortedList 개체의 인스턴스를 기본 생성자를 사용하여 만드는 방법이 있습니다. 기본 생성자는 정렬된 목록에게 시간순으로 날짜를 정렬하도록 지시합니다. 빈 정렬된 목록 개체를 $sortedList 변수에 저장합니다.

$sortedList = New-object system.collections.sortedlist

정렬된 목록 개체를 만들었으면 이를 채워야 합니다. 그러기 위해 ForEach 문을 사용하여 $entries 변수에 저장된 이벤트 로그 항목의 목록을 차례로 살펴봅니다. 모음을 차례로 살펴보는 동안 $event 변수는 해당 모음에서의 현재 위치를 추적합니다. Add 메서드를 사용하여 System.Collections.SortedList 개체의 두 속성을 추가할 수 있습니다. 정렬된 목록에서는 키와 값 속성을 추가할 수 있습니다. Dictionary 개체와 비슷하지만, 배열처럼 모음을 인덱싱할 수 있다는 점이 다릅니다. timegenerated 속성을 키로, 이벤트 ID를 값 속성으로 추가합니다.

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated,
 $event.eventID )
} #end foreach event

다음에는 서버의 현재 가동 시간을 계산합니다. 이를 위해 정렬된 목록에서 가장 최근의 이벤트 로그 항목을 사용합니다. 이 항목은 항상 6005 인스턴스입니다. 가장 최근의 항목이 6006이라면 서버는 계속 중단된 상태일 것입니다. 이 인덱스는 제로(0) 기반이므로 가장 최근의 항목은 카운트 –1이 됩니다.

시간 생성 값을 검색하려면 정렬된 목록의 키 속성을 확인해야 합니다. 인덱스 값을 얻으려면 카운트 속성을 사용하여 그 속성에서 1을 뺍니다. 그런 다음 앞서 채웠던 $currenttime 변수에 저장된 날짜 시간 값에서 6005 이벤트가 생성된 시간을 뺍니다. 스크립트가 디버그 모드에서 실행된 경우에만 이 계산의 결과를 출력할 수 있습니다. 이 코드는 다음과 같습니다.

$uptime = $currentTime -
$sortedList.keys[$($sortedList.Keys.Count-1)]
Write-Debug "Current uptime $uptime"

이제 정렬된 목록 개체를 살펴보면서 서버의 가동 시간을 계산할 차례입니다. System.Collections.Sorted list 개체를 사용하고 있으므로 목록 인덱싱이 가능하다는 점을 활용합니다. 이를 위해 카운트 -2에서 시작하여 for 문을 사용합니다. 카운트 -1은 현재 가동 시간을 구하는 데 사용했기 때문입니다.

역으로 계산하여 가동 시간을 얻을 것이므로, for 문의 두 번째 위치에 지정된 조건은 항목이 0보다 크거나 같을 때입니다. for 문의 세 번째 위치에서는 --를 사용합니다. 그러면 $item의 값이 1씩 감소합니다. 스크립트가 -debug switch와 함께 실행될 경우 Write-Debug cmdlet를 사용하여 인덱스 번호의 값을 출력할 수 있습니다. 또한 `t 문자를 사용하여 탭을 넣고 시간 생성 시간 값을 출력할 수 있습니다. 이 코드 섹션은 다음과 같습니다.

For($item = $sortedList.Count-2 ; $item -ge 
  0 ; $item--)
{ 
 Write-Debug "$item `t `t $($sortedList.
 GetByIndex($item)) `t `
   $($sortedList.Keys[$item])" 

eventID 값이 6005(시작 이벤트 ID 값)인 경우 이전의 가동 중지 값에서 시작 시간을 빼는 방법으로 가동 시간의 양을 계산할 수 있습니다. 이 값을 $uptime 변수에 저장합니다. 디버그 모드인 경우 Write-Debug cmdlet를 사용하여 이 값을 화면에 출력할 수 있습니다.

 if($sortedList.GetByIndex($item) -eq $startUpID)
 {
  $uptime += ($sortedList.Keys[$item+1] 
  - $sortedList.Keys[$item])
  Write-Debug "adding uptime. `t uptime is now: $uptime"
 } #end if  
} #end for item

마지막으로, 보고서를 생성해야 합니다. 시스템 환경 변수 컴퓨터에서 컴퓨터 이름을 가져옵니다. $startingdate 값에 저장된 현재 시간을 사용하며, 해당 기간의 총 가동 시간(분)을 표시합니다. 형식 지정자 {0:n2}를 사용하여 두 자리 숫자로 출력합니다. 그 다음에는 가동 시간(분)을 나타내는 숫자를 보고서 대상 기간(분)을 나타내는 숫자로 나누어 가동 중지 시간의 비율(%)을 계산합니다. 동일한 형식 지정자를 사용하여 소수점 이하 두 자리로 출력합니다. 아래와 같이 가동 시간의 비율(%)을 계산한 다음 두 값 모두 출력할 수도 있습니다.

"Total up time on $env:computername since $startingDate is " + "{0:n2}" -f `
  $uptime.TotalMinutes + " minutes."
$UpTimeMinutes = $Uptime.TotalMinutes
$percentDownTime = "{0:n2}" -f (100 - ($UpTimeMinutes/$minutesInPeriod)*100)
$percentUpTime = 100 - $percentDowntime
"$percentDowntime% downtime and $percentUpTime% uptime."

이제 Scripting Guys는 최초의 질문으로 돌아가겠습니다. 언제 위(up)가 아래(down)가 됩니까? 가동 중지 시간을 고려하지 않고서는 가동 시간에 대해 얘기할 수 없음을 이제 아셨을 것입니다. 이 내용이 재미있었다면 TechNet의 또 다른 "Hey, Scripting Guy" 칼럼을 읽어 보시거나 스크립트 센터를 들러 주십시오.

버전 문제

객원 편집자 Michael Murgolo가 자신의 랩톱에서 Calculate­System­Up­timeFromEventLog.ps1 스크립트를 테스트하던 중 매우 귀찮은 오류가 발생했습니다. 그 스크립트를 제 친구 Jit에게 주었더니 그 역시 동일한 오류를 경험했습니다. 어떤 오류였을까요? 다음과 같습니다.

PS C:\> C:\fso\CalculateSystemUpTimeFromEventLog.ps1
Cannot index into a null array.
At C:\fso\CalculateSystemUpTimeFromEventLog.ps1:36 char:43 + $uptime = 
$currentTime - $sortedList.keys[$ <<<< ($sortedList.Keys.Count-1)]
Total up time on LISBON since 09/02/2008 00:00:00 is 0.00 minutes.
100.00% downtime and 0% uptime.

"Cannot index into a null array(null 배열을 인덱싱할 수 없음)"이라는 오류는 배열이 제대로 만들어지지 않았다는 뜻입니다. 그래서 저는 그 배열을 만드는 코드를 파헤쳐 봤습니다.

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated,
 $event.eventID )
} #end foreach event

결론적으로 코드는 정상이었습니다. 오류의 원인은 무엇이었을까요?

그 다음에는 SortedList 개체를 살펴보기로 했습니다. 이를 위해 System.Collections.SortedList 클래스의 인스턴스를 만들고 몇 가지 정보를 추가하는 단순한 스크립트를 작성했습니다. 이 시점에서 키의 목록을 출력하기 위해 키 속성을 사용했습니다. 코드는 다음과 같습니다.

$aryList = 1,2,3,4,5
$sl = New-Object Collections.SortedList
ForEach($i in $aryList)
{
 $sl.add($i,$i)
}

$sl.keys

제 컴퓨터에서는 이 코드가 제대로 작동했고 Jit의 컴퓨터에서는 실패했습니다. 하지만 적어도 이 사실에서 올바른 방향을 찾을 수 있었습니다. 문제는 바로 Windows PowerShell 1.0의 System.Collections.SortedList에 있는 버그입니다. 어쩌다가 저는 아직 릴리스되지 않은 Windows PowerShell 2.0의 최신 빌드를 실행했던 것입니다. 이 버전에서는 버그가 수정되었기 때문에 코드는 제대로 실행됩니다.

그렇다면 우리의 스크립트는 어떻게 되는 걸까요? 아시다시피 SortedList 클래스는 GetKey라는 메서드가 있으며, 이 메서드는 Windows Power­Shell 1.0과 Windows Power­Shell 2.0에서 모두 작동합니다. 따라서 1.0 버전의 스크립트에서는 키 모음을 하나씩 살펴보지 않고 대신 GetKey를 사용하도록 코드를 수정합니다. 2.0 버전의 스크립트에서는 Windows PowerShell 버전 2.0을 요구하는 태그를 추가합니다. 이 스크립트를 Windows PowerShell 1.0 시스템에서 실행하려고 하면 스크립트는 종료할 뿐이며 오류도 발생하지 않습니다.

Michael 역시 버그는 아니지만 설계 고려 사항과 관련 있는 점을 지적했습니다. 그에 따르면, 컴퓨터를 최대 절전 또는 대기 모드로 전환할 경우 이 스크립트는 가동 시간을 제대로 인식하지 못합니다. 맞는 말입니다. 이 이벤트를 인식하거나 찾지 않으니까요.

그러나 실제로 저는 제 랩톱이나 데스크톱 컴퓨터의 가동 시간에 별로 신경쓰지 않습니다. 오로지 서버의 가동 시간이 제 관심사이며, 저는 아직 대기 모드나 최대 절전 모드가 되는 서버를 본 적이 없습니다. 물론 그런 서버도 존재할 수 있습니다. 데이터 센터의 전기를 절약할 수 있는 희한한 방법이겠지만, 전 지금까지 그런 상황을 경험하지 못했습니다. 서버를 최대 절전 모드로 운영하고 계시는 분이 있으면 알려 주십시오. 제 연락처는 Scripter@Microsoft.com입니다.

Dr. Scripto의 Scripting Perplexer

매달 퍼즐 풀기 실력뿐만 아니라 스크립팅 능력까지 시험해 볼 수 있는 문제입니다.

2008년 12월: PowerShell 명령

아래의 목록은 21개의 Windows PowerShell 명령으로 구성되어 있습니다. 사각형에는 동일한 명령이 들어 있으나 숨겨져 있습니다. 가로, 세로 또는 대각선 방향으로(앞이나 뒤로) 숨겨져 있을 명령을 찾아보십시오.

EXPORT-CSV FORMAT-LIST FORMAT-TABLE
GET-ACL GET-ALIAS GET-CHILDITEM
GET-LOCATION INVOKE-ITEM MEASURE-OBJECT
NEW-ITEMPROPERTY OUT-HOST OUT-NULL
REMOVE-PSSNAPIN SET-ACL SET-TRACESOURCE
SPLIT-PATH START-SLEEP STOP-SERVICE
SUSPEND-SERVICE WRITE-DEBUG WRITE-WARNING

\\msdnmagtst\MTPS\TechNet\issues\en\2008\12\HeyScriptingGuy - 1208\Figures\puzzle.gif

정답:

Dr. Scripto의 Scripting Perplexer

정답: 2008년 12월: PowerShell 명령

fig12.gif

Ed Wilson은 Microsoft의 수석 컨설턴트이자 유명한 스크립팅 전문가입니다. Microsoft Certified Trainer이기도 한 그는 전세계 Microsoft 프리미어 고객들을 대상으로 인기 높은 Windows PowerShell 워크샵을 제공합니다. 그는 Windows 스크립팅에 관한 책을 비롯하여 8권의 책을 썼으며 약 12권의 다른 책에도 기고한 바 있습니다. Ed는 20개가 넘는 산업 분야 자격증을 보유하고 있습니다. 언어의 달인인 Craig Liebendorfer는 오랫동안 Microsoft 웹 편집자로 일해 왔습니다. 매일 글을 쓰면서 돈을 버는 직업이 있다는 사실을 그는 아직도 믿을 수 없어 합니다. 그가 좋아하는 것 중 하나가 인습을 타파하는 유머인 만큼 그는 이 자리에 안성맞춤입니다. Craig는 자신의 딸이 그의 인생에서 거둔 최대 업적이라 생각한다고 합니다.

언어의 달인인 Craig Liebendorfer는 오랫동안 Microsoft 웹 편집자로 일해 왔습니다. 매일 글을 쓰면서 돈을 버는 직업이 있다는 사실을 그는 아직도 믿을 수 없어 합니다. 그가 좋아하는 것 중 하나가 인습을 타파하는 유머인 만큼 그는 이 자리에 안성맞춤입니다. Craig는 자신의 딸이 그의 인생에서 거둔 최대 업적이라 생각한다고 합니다.