Hey, Scripting Guy!그리 힘들지 않은 일에도 보상은 있다

Microsoft Scripting Guys

이 기사의 코드 다운로드: HeyScriptingGuy2008_06.exe (150KB)

지난 수백 년 동안 사람들은 힘들게 일하면 그만큼의 보상이 따르며, 진정한 만족은 하루를 땀 흘리며 정직하게 일하는 데 있고, 진정한 행복은 여정이 아닌 목적지에서 도달했을 때 얻어진다는 등(이해가 되시죠?)에 대한 믿음을 가지고 있습니다. 신데렐라처럼 불평 없이 고된 일을 묵묵히 해내면 언젠가는 그만한 댓가가 주어질 것입니다.

참고: 과연 그럴까요? 여러분이 열심히 일하면 언젠가는 더 행복한 날이 오고 노력의 댓가가 있을까요? 왜 이와 같은 질문을 하냐구요?

물론, 신데델라의 경우에는 운이 좋았습니다. 어쨌든 여러분도 사악한 의붓 어머니 및 의붓 언니들과 살게 된다면 열심히 일할 수 있는 모든 기회를 갖게 됩니다. 하지만 신데렐라처럼 운이 좋지 않으면 어떻게 될까요? 사악한 의붓 어머니 및 의붓 언니와 살지 않는다면 어떻게 될까요? 여러분이 가혹하고 보람 없는 긴 고된 나날을 보내게 된다면 어떻게 될까요? 과연 진정한 행복은 어떻게 찾아야 할까요?

뼈빠지게 일하겠다는 확고한 계획이 있다면 Scripting Guys는 그림 1처럼 멋진 테이블 형식으로 데이터를 출력하는 스크립트 작성을 시도해 보라고 제안하겠습니다.

그림 1 테이블 형식의 출력

그림 1** 테이블 형식의 출력 **(더 크게 보려면 이미지를 클릭하십시오.)

앞에서 말했듯이 신데렐라는 운이 매우 좋은 사람이었습니다. 그녀는 집의 굴뚝부터 바닥까지 청소했으며, 청소가 끝나면 재와 검댕을 뒤집어쓰고 화로 앞에 앉아 있어야 했습니다. 하지만 신데렐라도 테이블 형식으로 데이터를 표시할 수 있는 스크립트를 작성해야 했다면 좌절했을 것입니다. 재와 검댕을 뒤집어쓰고 앉아 있는 것과 데이터를 테이블 형식으로 출력하는 스크립트를 작성하는 것은 전혀 다른 문제입니다.

참고: 스크립트를 작성하기 위해 화로 앞에 앉아야 하는 경우만 아니라면 두 문제는 거의 같다고 볼 수 있습니다.

신데렐라가 이 작업에서 왜 좌절하게 될까요? 저주스러울 정도로 너무 어려우니까요. VBScript에서 데이터를 테이블 형식으로 표시할 수 있는 유일한 방법은 다음 작업을 차례로 실행하는 것입니다.

  • 먼저 각 열의 최대 크기를 결정합니다. 예를 들면 서비스의 표시 이름으로 52자 공간을 채우고자 할 수도 있습니다.
  • 그런 다음 데이터 하나의 문자 수를 계산합니다. 예를 들면 표시 이름 Adobe LM Service는 16자로 되어 있습니다.
  • 표시 이름이 52자 제한을 초과할 경우 할당된 공간에 맞게 문자열 끝부분에서 잘라내야 하는 문자 수를 결정합니다. 표시 이름이 52자 공간을 초과하지 않는 경우 정확히 52자가 되도록 문자열 끝부분에 추가할 공백 수를 결정합니다.
  • 다음 데이터 집합에 대해서도 위 작업을 반복합니다. 그리고 나서 그 다음 데이터 집합으로 넘어갑니다. 이런 식으로 계속 반복합니다.

신데렐라 이야기에 대해 잘 알고 있다면 주인공을 돕는 요정이 갑자기 나타나서 정말 간단히 쥐를 시스템 관리자로 변신시켜 쥐가 여러분을 대신하여 이 스크립트를 작성하게 할 수도 있다고 생각할 것입니다. 하지만 아마도 그런 일은 기대하지 않는 것이 좋을 것입니다. 여기서는 아마도 여러분 스스로 해결해야 할 것입니다.

하지만 긍정적으로 본다면 여러분은 세상에서 가장 큰 만족감을 얻는 사람이 될 것입니다. 왜냐하면 세상에서 가장 열심히 일하는 사람이 될테니까요. 아주 열심히 일해야 합니다.

물론 일부 사람들의 경우 이같이 그렇게 힘든 일을 피할 수 있는 기회가 있다면 자신의 만족감 중 어느 정도는 기꺼이 포기하려 할 수도 있을 것입니다. 여러분이 이러한 부류 중 한 사람이라면 아마도 이렇게 생각할지도 모릅니다. "Scripting Guys가 있어서 정말 다행이야. 이들이 내게 쥐를 시스템 관리자로 변신시키는 방법을 알려주고 이 쥐를 시켜 테이블 형식으로 출력하는 방법을 알려줄거야." 하지만, 유감스럽게도 나쁜 소식을 전해드려야 할 것 같습니다. Scripting Guys는 쥐를 시스템 관리자로 변신시키는 방법을 모릅니다. 하지만 여러분이 원한다면 시스템 관리자를 쥐로 바꿀 수는 있습니다. Scripting Guys는 데이터를 테이블 형식으로 출력할 스크립트는 물론이고, 여러분을 대신하여 스크립트를 작성할 누군가를 찾을 수 있는 방법도 모릅니다.

하지만 괜찮습니다. Windows® XP 또는 Windows Server® 2003(여러분 중 대부분이 사용)을 실행하는 경우라면 여러분을 대신하여 스크립트를 작성해 줄 쥐가 필요하지 않습니다. (죄송하지만 이것은 Windows Vista®에는 해당되지 않습니다.) 대신, Microsoft.CmdLib 개체 덕분에 최소한의 노력만으로 여러분 스스로 이 작업을 수행할 수 있습니다. 그림 2의 샘플 스크립트를 살펴보겠습니다.

Figure 2 테이블 형식으로 출력하기

Dim arrResultsArray()
i = 0

Set objCmdLib = _
  CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = _
  WScript.Application

arrHeader = Array("Display Name", _
  "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" _
  & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service")

For Each objService In colServices
  ReDim Preserve arrResultsArray(i)
  arrResultsArray(i) = _
  Array(objService.DisplayName, _
    objService.State,objService.StartMode)
  i = i + 1
Next
objCmdLib.ShowResults arrHeader, _
  arrResultsArray, arrMaxLength, _
  strFormat, blnPrintHeader, arrBlnHide

궁금해 하실 분들을 위해 미리 말씀 드리자면, Microsoft.CmdLib는 Windows XP 및 Windows Server 2003과 함께 제공되는 COM 개체입니다. 이 개체에는 다양한 기능이 있으며, 자세한 정보를 보려면 명령 프롬프트에서 cmdlib.wsc /?를 입력한 후 화면에 표시되는 스크립트 파일의 설명을 읽어보시기 바랍니다. 여기서는 Microsoft.CmdLib 기능 중 데이터를 테이블 형식으로 표시하는 기능에만 관심이 있습니다.

그렇다면 대체 데이터를 테이블 형식으로 어떻게 표시합니까? 자 그럼 그 방법에 대해 알아보겠습니다. 보시는 것처럼 샘플 스크립트는 arrResultsArray라는 동적 배열을 정의함으로써 시작됩니다.

Dim arrResultsArray()

일반적인 스크립트에서 데이터는 검색될 때 화면에 신속히 다시 출력됩니다. 하지만 왜 Scripting Guys가 일반적인 방식으로 작업을 수행할 것이라고 생각하십니까? 이 스크립트에서는 데이터가 검색될 때 데이터가 다시 화면에 출력되지 않도록 할 것입니다. 대신, 반환된 모든 데이터를 배열 내에 저장한 후 Microsoft.CmdLib를 통해 데이터의 서식을 지정하여 표시하도록 합니다.

다시 말하면, 이러한 이유로 동적 배열(스크립트 과정 중에 크기를 변경할 수 있는 배열)을 작성하는 일부터 시작합니다. 배열을 정의한 후에는 i라는 카운터 변수 값을 0으로 설정하고 이 변수를 사용하여 배열의 현재 크기를 추적할 것입니다.

궁금해 하실 분들을 위해 미리 말씀 드리자면, 배열의 첫 번째 항목에는 항상 인덱스 번호 0이 지정되므로 i 값을 0으로 설정하는 것입니다. 따라서 첫 번째 항목을 배열에 추가할 때 항목 1이 아닌 항목 0을 추가합니다.

다음에는 Microsoft.CmdLib 개체 인스턴스를 초기화해야 하므로 다음 두 줄의 코드가 필요합니다.

Set objCmdLib = _
CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = WScript.Application

그럼 다음과 같은 작은 코드 블록이 필요합니다.

arrHeader = Array("Display Name", "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

여기서는 출력 구성을 위한 몇 가지 매개 변수를 설정합니다. 첫 번째 줄에서는 arrHeader라는 배열에 값을 할당합니다. 예상한 대로 이 값이 출력 테이블 내 각 열의 머리글입니다. 이 샘플 스크립트의 경우, 컴퓨터에서 실행되는 서비스에 대한 정보를 검색한 다음 각 서비스에 대한 DisplayName, State 및 StartMode 속성 값을 표시하려고 합니다.

크게 놀랄 것도 없겠지만, 배열에 열 이름 Display Name, State 및 Start Mode를 할당합니다. 원한다면 A, B 및 C, Larry, Moe 및 Curly 또는 그 밖의 원하는 이름을 열에 쉽게 할당할 수 있습니다. 열 이름이 속성 이름과 같을 필요는 없습니다.

참고: 의붓 언니의 이름을 비롯하여 신데렐라 이야기에는 다양한 여러 변형 요소가 있습니다. 신데렐라의 디즈니 버전에서 의붓 언니 이름은 Drizzella와 Anastasia인데, 글쎄 이 이름이 저희 스크립트 편집자의 첫 번째 이름과 중간 이름입니다.

코드의 두 번째 줄에서는 arrMaxLength라는 또 다른 배열에 값을 할당합니다. 이 배열에는 테이블 내 각 열의 크기를 저장합니다. 출력의 열 1(서비스 표시 이름)에는 52자 공간을 할당하려고 합니다. 그런 다음 서비스 상태 및 시작 모드에는 각각 12자 공간을 할당하려고 합니다. Microsoft.CmdLib에서 열 사이에 빈 공간을 자동으로 삽입합니다. 열 크기를 52, 12 및 12자 공간으로 설정하려는 경우 다음과 같은 코드를 사용하면 됩니다.

arrMaxLength = Array(52, 12, 12)

세 번째 줄은 데이터의 출력 형식을 지정합니다. 데이터를 테이블로 표시하려고 하므로 strFormat(출력 유형을 저장하는 변수)의 값을 Table로 설정합니다. 데이터를 쉼표로 구분된 값 목록으로 표시하려고 한다고 가정해 보겠습니다. 이 경우에는 다음과 같이 형식을 CSV로 설정해야 할 것입니다.

strFormat = "CSV"

이렇게 하면 다음과 비슷한 출력을 얻게 됩니다.

"Display Name","State","Start Mode"
"Adobe LM Service","Stopped","Manual"
"Adobe Active File Monitor V4","Stopped","Manual"
"Alerter","Stopped","Manual"
"Application Layer Gateway Service","Running","Manual"
"Apple Mobile Device","Running","Auto"
"Application Management","Stopped","Manual"

Microsoft.CmdLib에서 각 항목 사이에 쉼표를 추가할 뿐만 아니라 개별 값을 따옴표로 묶었습니다. 이 기능은 처음에 생각했던 것보다 더 멋진 기능입니다. 즉, 이 작업을 수동으로 수행하려면 다음과 같은 코드를 사용해야 할 것입니다.

Wscript.Echo Chr(34) & objService.DisplayName & Chr(34) & "," & Chr(34) & objService.State & Chr(34) & "," & Chr(34) & objService.StartMode & Chr(34)

역시 이 방법도 안 되겠군요.

그런데 테이블을 좋아하지 않는 경우, CSV 형식도 좋아하지 않을까요? 그렇다면 형식을 List로 설정해 보십시오. 그러면 다음과 같은 출력을 얻게 됩니다.

Display Name: Adobe LM Service
State:        Stopped
Start Mode:   Manual
Display Name: Adobe Active File Monitor V4
State:        Stopped
Start Mode:   Manual

하지만 본론을 벗어난 내용이네요. (가끔씩 이렇게 본론에서 벗어나게 되죠.) 출력 형식을 정의한 후에는 blnPrintHeader라는 변수의 값을 True로 설정합니다.

blnPrintHeader = True

blnPrintHeader를 사용하여 Microsoft.CmdLib에 열 머리글을 출력하도록 지시하려고 합니다. 열 머리글 출력을 원하지 않는다면 어떻게 해야 하나요? 괜찮습니다. 이 경우에는 blnPrintHeader를 False로 설정하면 됩니다.

blnPrintHeader = False

마지막으로 다음과 같은 코드가 있습니다.

arrBlnHide = Array(False, False, False)

데이터를 표시할 때가 되면 Microsoft.CmdLib에서 열을 표시할지 숨길지 선택할 수 있는 옵션을 제공합니다. 특정 속성에 대한 데이터를 표시하지 않기 위해 열을 숨기려면 해당 속성의 값을 True로 설정합니다. 열을 표시하려면 해당 값을 False로 설정합니다.

출력에서 DisplayName, State 및 StartMode 속성의 값을 순서대로 표시하려 하므로 배열에서 False, False, False 값을 사용합니다. DisplayName을 표시하고, State를 숨기며 StartMode를 표시하려는 경우에는 어떻게 해야 하나요? 이 경우에는 다음 코드를 사용하면 됩니다.

arrBlnHide = Array(False, True, False)

열을 표시하려면 False를, 열을 숨기려면 True를 사용한다는 것을 기억하시기 바랍니다.

이제 데이터를 출력할 준비가 되었습니다. 실제로 데이터가 있다면요. (Scripting Guys가 데이터를 출력하지 못하는 스크립트를 작성한 적이 있나요? 이로 인해 엄청난 시간을 들여 스크립트를 디버깅하려고 했으나, 알고보니 처음부터 실제로 검색할 데이터가 없었던 적은 없나요? 물론 아니겠죠. 그런데 어떻게 장담하시나요?)

이를 고려하여, 다음 단계는 로컬 컴퓨터에서 Windows Management Instrumentation(WMI) 서비스에 바인딩한 후 ExecQuery 메서드를 사용하여 해당 컴퓨터에 설치된 모든 서비스에 대한 정보를 검색하는 것입니다.

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery ("Select * FROM Win32_Service")

이 작업이 WMI 데이터로만 제한되는 것은 아닙니다. 예상한 대로 Microsoft.CmdLib는 모든 종류의 데이터에 사용할 수 있습니다. WMI는 일련의 데이터를 검색하기 위한 한 가지 유용한 방법일 뿐입니다.

서비스 관련 데이터 컬렉션이 있으면 For Each 루프를 설정하여 컬렉션의 모든 서비스를 순환할 수 있습니다. 하지만 각 서비스의 속성 값을 다시 화면에 출력하는 대신 다음 코드를 실행합니다.

ReDim Preserve arrResultsArray(i)
arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)
i = i + 1

이 코드 블록에서 수행하는 작업은 무엇일까요? 첫 번째 줄에서는 ReDim Preserve 명령을 사용하여 배열 크기를 조정하고 해당 배열에 있는 기존 데이터를 유지합니다. Preserve 키워드가 없으면 배열 크기는 조정되지만 해당 배열에 현재 들어 있는 데이터가 지워집니다.

만들려는 배열의 크기는 어떻게 됩니까? 루프를 처음 실행할 때는 크기가 0인 배열을 만듭니다. 다시 말해서 하나의 요소만 포함하는 배열을 만드는 것입니다. 크기가 0인 배열이 만들어진다는 것을 어떻게 확실히 알 수 있을까요? 카운터 변수 i를 사용하고 있으며 이 변수의 값이 0이기 때문입니다.

그렇다면 변수 i를 사용하여 배열 크기를 나타내는 경우 크기가 항상 0으로 설정됨을 의미하나요? 글쎄요. 루프를 실행할 때마다 i의 값을 1씩 증가시키는 것을 제외하고는 그렇다고 볼 수 있습니다. 보시는 것처럼 코드 블록의 세 번째 줄에서 이 작업을 수행합니다.

이제 우리가 걱정해야 할 코드를 한 줄만 남겨 놓고 있습니다.

arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)

여기서는 단순히 위에서 언급했던 DisplayName, State 및 StartMode라는 속성 값을 사용하며 이러한 속성 값을 arrResultsArray에 추가하려고 합니다. 속성 값을 개별적으로 추가하지 않고, 대신에 값 배열로 추가합니다. 이 부분은 다소 일반적이지 않을 것입니다. 즉 arrResultsArray 배열의 각 항목이 또 하나의 배열이 됨을 의미합니다. 이는 단순히 Microsoft.CmdLib의 작동 방식일 뿐입니다.

물론, 이 부분으로 인해 다시 걱정이 생길 수도 있을 것입니다. "배열의 배열? 전체가 배열로 이루어진 배열에서 모든 개별 값에 액세스하려면 도대체 어떻게 해야 할까요?" 겁먹지 마세요. 매우 간단합니다. 그냥 Microsoft.CmdLib에 맡기면 됩니다.

참고: 안타깝게도, 배열로 이루어진 배열의 모든 개별 값에 액세스하는 일은 Microsoft.CmdLib가 여러분을 위해 할 수 있는 유일한 일입니다. 하지만, 개체의 다음 릴리스에서는 사악한 의붓 엄마의 침실 청소, 설거지, 못된 언니들의 옷 만들기 등 다른 기능도 고려될 것입니다.

사실 ShowResults 메서드를 호출한 후 이 메서드를 스크립트 앞쪽에서 구성한 모든 배열 및 변수에 전달하면 간단히 데이터를 깔끔한 테이블 형식으로 출력할 수 있습니다.

objCmdLib.ShowResults arrHeader, arrResultsArray, arrMaxLength, strFormat, blnPrintHeader, arrBlnHide

어떻게 보일까요? 그림 1에서 보고 기대했던 멋진 형식의 출력처럼 보일 것입니다.

나쁘지 않죠? 멋진 출력을 얻었지만 거의 별도의 작업이 필요하지 않았습니다. 다음 스크립트는 이 첫 번째 스크립트를 조금만 변경하면 되므로 훨씬 더 쉬울 것입니다.

물론, 신데렐라가 스크립트를 테이블에 출력할 수 있었을 때나 그녀가 하루 종일 그리고 밤새도록 노예 생활을 하며 느꼈을 똑같은 만족감을 여러분은 얻지 못할 것입니다. 솔직히 말하면, 아마도 여러분은 왕자와 결혼하지 못할 것입니다. 이건 흥정에 포함되는 부분이 아니니까요. 하지만 이건 특히 왕자가 요즘과 같은 세상에서는 어떤 것을 좋아할지 고려하여 아주 싸게 VBScript 스크립트에서 멋진 출력을 사는 것과 같습니다.

그리고 누가 알겠습니까? 결국 여러분 스스로 왕자를 차지하게 될지 말입니다. 아무튼 왕족이라도 컴퓨터에 설치된 모든 서비스에 대한 정보가 필요합니다. 그렇죠?

Dr. Scripto의 Scripting Perplexer

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

2008년 6월: PowerShell Pathway

이러한 각 퍼즐에서 글자를 가로, 세로 및 대각선 방향으로 연결하여 Windows PowerShell cmdlet의 이름을 만듭니다. 각 글자는 한 번씩만 사용할 수 있습니다. 다음은 퍼즐 풀이 예입니다.

이 퍼즐에서 만들어진 cmdlet는 New-Alias입니다. 이제 여러분 차례입니다. 다음 세 가지 퍼즐을 풀어보세요.

ANSWER:

Dr. Scripto의 Scripting Perplexer

정답: PowerShell Pathway, 2008년 6월

Read-Host

Set-AuthenticodeSignature

Microsoft Scripting Guys는 Microsoft에 고용된 직원들로, 이들은 좋아하는 야구 경기와 기타 여러 활동을 하는 시간을 제외하고는 항상 TechNet 스크립트 센터를 운영합니다. 자세한 내용은 www.scriptingguys.com에서 확인하십시오.

© 2008 Microsoft Corporation 및 CMP Media, LLC. All rights reserved. 이 문서의 전부 또는 일부를 무단으로 복제하는 행위는 금지됩니다..