嗨,Scripting Guy!

嗨,Scripting Guy!

歡迎使用《TechNet 專欄》,Microsoft Scripting Guys 會在此為您解答有關系統管理指令碼的常見問題。您有關於系統管理指令碼方面的問題嗎?請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

還有,別忘了瞧瞧全新經過改良的嗨,Scripting Guy!過往文件

今天的問題:如何使用 Windows PowerShell 在遠端電腦上啟動服務?


如何使用 Windows PowerShell 在遠端電腦上啟動服務?

嗨,Scripting Guy!如何使用 Windows PowerShell 在遠端電腦上啟動服務?

-- NA

NA,您好。幾年前一位叫做法蘭西斯.福山 (Francis Fukuyama) 的教授出了一本書,叫做《歷史之終結與最後一人》,聲稱冷戰已經落幕,從此將風平浪靜,太陽底下不再有新鮮事了。Scripting Guy 和福山教授又扯上什麼關係?當然有,哈!

Scripting Guy 好大的膽子,竟敢 (代表全人類) 挑戰這位當代的學術泰斗?至少我們敢打賭法蘭西斯.福山先生沒有常讀本專欄,最重要的是我們已經證明了太陽底下依然有新鮮事!現在我們不止歡慶「嗨,Scripting Guy!」推出第 500 篇專欄 (英文),也回答第一個與 Windows PowerShell 有關的問題。接招吧,法蘭西斯.福山先生!

附註:啥?您從來沒聽過 Windows PowerShell?您不知道 Windows PowerShell 的用途是什麼嗎?建議您先讀一讀新的 Script Center 系列文章《What Can I Do With Windows PowerShell?》(英文)。


您嘗試使用 Start-Service (英文) cmdlet 在遠端電腦重新啟動服務時,使用的命令如下 (指令碼已經過簡化以便示範):

Get-WmiObject -computer atl-fs-01 Win32_Service -Filter "Name='Alerter'" | Start-Service

換句話說,您想連接至 atl-fs-01 電腦上的警訊器 (Alerter) 服務,然將物件「輸送」至 Start-Service cmdlet。您可以順利地連至遠端電腦上的警訊器服務,但是呼叫 Start-Service 時命令卻失敗了,怪的是 Start-Service cmdlet 明明可以在您的本機電腦上使用。

而這就是問題所在。Windows PowerShell 是一項強大有趣的新技術,但是 (在撰文的同時) 仍然是 Beta 版,一些計畫納入產品發行版本中的功能尚未成熟,不巧其中一項就是針對遠端電腦執行 cmdlets 的功能。有朝一日您能夠使用如下命令在遠端電腦上啟動服務:

TechNet Script Center

Start-Service alerter -computer atl-fs-01

但不是現在。

儘管如此,我們還是可以在遠端電腦上取得警訊器服務的資訊。這又是怎麼一回事呢?原來 Get-WMIObject (英文) cmdlet 並不在限制之內,Get-WMIObject 可以用 piggybacks 進入 WMI,借用 WMI 的能力操作遠端電腦:無須依賴 Windows PowerShell cmdlets 連線,Get-WMIObject 自己就可以用 WMI 完成一樣的工作。

這裡有一個原則:只要您用的是 WMI 屬性和方法,就可以靠 Get-WMIObject 操作遠端電腦。否則 Windows PowerShell 只能用來操作本機電腦。(雖然我們不清楚 Windows PowerShell 何時能夠操作遠端電腦,但是這一點有朝一日會改進。)

使用 Get-WMIObject 搭配標準的 WMI 方法 (例如 StartService) 在遠端電腦上啟動服務,背後有什麼意義嗎?當然有意義,而且其中還有玄機,咱們先來瞧瞧在遠端電腦啟動服務的 Windows PowerShell 指令碼/命令,再給讀者說明:

(Get-WmiObject -computer atl-fs-01 Win32_Service -Filter "Name='Alerter'").InvokeMethod("StartService",$null)

這個命令是由兩個部分組成,一個連接至警訊服務,另一個啟動服務。先介紹第一部份,連接至 WMI 服務:

(Get-WmiObject -computer atl-fs-01 Win32_Service -Filter "Name='Alerter'")

連線至 atl-fs-01 電腦上的 WMI 服務,然後把 Name 屬性等於 AlerterWin32_Service 類別通通擷取下來。這裡的動作用 VBScript 程式碼表達就相當於:

Set objWMIService = GetObject("winmgmts:\\atl-fs-01")
Set colServiceList = objWMIService.ExecQuery _
    ("Select * From Win32_Service Where Name='Alerter'")

看清楚囉,我們並不呼叫 Windows PowerShell 方法裡的 ExecQuery 方法,而是使用 –Filter 參數,指定只需要傳回 Name 等於 Alerter 的服務。

問得好:Get-WMIObject 陳述式為何要用括弧括住呢?這是為了確保 Windows PowerShell 把傳回的物件「當作物件看待」,以便用來呼叫方法。取得物件之後,在右括弧後加上一個點 (.),一如 VBScript 中的作法。在 Windows PowerShell 命令中輸入:

(Get-WmiObject -computer atl-fs-01 Win32_Service -Filter "Name='Alerter'").InvokeMethod("StartService",$null)

如果是 VBScript 則輸入 (大多數在 For Each 迴圈內):

objItem.StartService

懂了嗎?好,更麻煩的還在後頭。命令的第二個部分中,並不直接呼叫 StartService 方法,而是:

InvokeMethod("StartService",$null)

這行的目的何在?這麼說好了,呼叫 Get-WMIObject 後的確傳回了物件,但這是 .NET Framework 物件 (明確的說,是 System.Management.ManagementObject#root\cimv2\Win32_Service object)。沒辦法呼叫 StartService 方法,原因很簡單:這類物件沒有 StartService 方法。所以我們改呼叫 InvokeMethod 方法,傳出兩個參數:

  • "StartService",想叫用的 WMI 方法名稱。
  • $Null,也就是 Null 值。

InvokeMethod 接受兩個參數:被呼叫的參數,後接方法所需的額外引數。本例中並沒有方法引數。但是不管參數存不存在,nvokeMethod 一定要兩個參數。為了滿足 InvokeMethod 的要求,我們傳一個 NULL 值給第二個參數。

又一個好問題:我們是怎麼知道傳回的物件是 System.Management.ManagementObject#root\cimv2\Win32_Service 物件,而且它不支援 StartService 方法呢?我是很想回答,「因為我們很聰明,無所不知」,不過應該沒人會信吧。那我們就老實回答好了,方法是取得指定物件,用 Get-Member (英文) cmdlet 跑過,取得屬性和方法清單。也就是:

Get-WmiObject Win32_Service -Filter "Name='Alerter'" | Get-Member

這裡用了一套老招數,從錯誤中不斷嘗試,直到找出啟動服務的方法。

在此有幾點要聲明,我們提出的方法的確可行,但並不表示這就是最佳或唯一可行的途徑。如果您有更棒的方法可以用 Windows PowerShell 達到相同的目的,請與我們分享,因為我們自己也正在摸索這項新技術。(福山博士,我們非常想聽一聽您的意見!)

為了使本文討論不要太過深入,我們跳過了 Windows PowerShell 的功能和原理這類的技術細節。這些資訊現在還不重要,因為我們的目的是讓讀者對 Windows PowerShell 的運作方式,以及在某些情況下仍無法運作 (至少是現在) 的原因有粗淺的認識。對了,我們還介紹了如何使用 Windows PowerShell 在遠端電腦上啟動服務。這就是今天的重點,

順便向世人證明太陽底下依然有新鮮事,這點我覺得表現得相當不錯。


如需詳細資訊

查看嗨,Scripting Guy!- 過往文件

 

回到頁首 回到頁首