UIFU.com

微软下一代 Windows 命令行 PowerShell 测试

2008-01-25 01:40:46 , Holeo , 新鲜科技

取代CMD.EXE的加强版命令行PowerShell

命令行模式都长得一个样,PowerShell可单独用自己的视窗执行,也可以在CMD.EXE之下操作。

图形化接口/界面(GUI,Graphics User Interface)是为了让使用电脑更方便,让人们不必去记一大堆指令,也能顺利的完成工作,但相形之下使用范围也容易受到限制,表单元件是焊死的。尽管图型与文字介面(CLI,Command Line Interface)有各自的思考模式与方便性,不过使用命令行的自由度确实比较高,比如说要在一分钟后无条件关机,可在命令行键入「shutdown –s –f –t 60」,如果换成视窗表单,就必须要有三个表单元件「重开机或关机→是否强制关机→时间」,相较之下便有它方便的地方,不然为什么大部份已经有X Window的Linux使用者还是喜欢开终端机来玩。XP提供的命令行功能阳春,难以实现较深入的系统管理,因此微软想出了以PowerShell来取代传统命令行的方桉。

不管你是命令行的偏执狂,或是喜欢享受敲敲打打的指令键入快感,那么一定不能不认识PowerShell。去研究「Shell」的字面意义不大,直接把它想成是「壳、层」的意思就好,也就是与作业系统沟通的介面。Shell并不一定代表纯文字的操控,DOS 6.0之前也有类似档桉总管的DOS Shell,但PowerShell的概念比较像是UNIX、Linux的csh/bash,以丰富的命令及整合script的执行环境,实现系统的自动化管理功能。

Server 2008 RC0 已经预设安装了PowerShell,但需手动开启。

就XP而言,并没有提供图形化介面的关机设定。手动用命令行进行并存成捷径是比较方便的方法。

开始使用PowerShell

PowerShell不用花钱买,只要有Windows XP(含)以上的作业系统,装了.NET Framework 2.0后,经过微软官方网站的认证就可以直接下载,安装完成后点击桌面的捷径便可执行,它会跳出一个类似命令行的执行环境。如果是Windows Server 2008则已内建整合在版本RC0中,但是预设为不安装,使用者需手动开启。在传统的命令行模式中,输入的指令可分为内部命令(如DIR、CLS、PROMPT)及外部命令(IPCONFIG、TASKKILL、XCOPY等)。基本上,内部命令是无法直接任意修改或增加的,外部命令不外乎就是一个可直接点击执行的档桉名称,如果输入了一个不属于内部,也不属于外部命令的字串,那么就会有指令无效的信息提示。然而PowerShell提供「cmdlet」的概念,让使用者可以自由加入新的内部命令。PowerShell的操作方式很直觉,支持基本的DOS及UNIX指令,如检视目录清单,可藉由「DIR」或「LS」两种方式实现,而cmdlet的结构,大致上可分为「动词+名词+管线」,如下面这样的一段字串:

envoke-item *.txt

envoke为动词,item为名词,*.txt是参数,此行的意义为开启该目录中所有的纯文字档,PowerShell会自动去找档桉类型的预设开启程式。因为记事本本身允许的命令行参数只能下一个,并无法将全部的文字档拖拉到记事本,或者是像这样输入:「notepad.exe 1.txt 2.txt 3.txt….」,记事本仍然只会开启一个档桉,这个动作很方便,尤其是要一次开启多个图片档编修时。看起来好像很简单?太无聊了?学东西要慢慢来,上面的范例没有用到管线的动作,事实上没有发挥PowerShell的特色,再来看下一个例子:

Ls *.doc | Where-Object {$_.LastWriteTime –like “2007/10/* *” | move-item –destination D:\DOC\2007年10月

「|」符号代表管线,管线的意思是将前一个命令的结果传给后面的命令做处理,上面指令代表的意义即为:先将目录中所有副档名为「.DOC」的档桉用「ls」命令行出来,再送给下一个命令「Where-Object」过滤出最后修改时间在2007年10月之内的档桉,然后将它们移到指定的资料夹存放,一行指令可以代替本来需要开视窗设定条件搜寻,并且手动搬移档桉的动作,整合到桌面的右键选单说不定还可以取代电脑王37期的「桌面清道夫」。

变数的使用

虽然在键盘上敲敲打打的很爽,但是遇到重覆的指令输入时就变得很蠢,PowerShell提供以变数来储存指令及物件的参照,如下面的例子:

$ie = New-Object -ComObject InternetExplorer.Application

$ie.navigate(“http://www.pchome-advance.com.tw”)

$ie.navigate(“http://www.pchome-advance.com.tw/stuff01.rar”)

指定变数时,请在名称前面加个「$」,上面函式的内容为建立一个IE物件,这时候一个IE视窗会被开启,不过并不会显示在萤幕上,连工作列的图示都不会出现。接着连上指定的网址,并下载档桉。以上的这些动作,都是在幕后进行的,用来当老闆模式「BOSSKEY」颇管用,要把视窗叫出来,需键入「$ie.visible=$true」,再次隐藏则可以打「$ie.visible=$false」,好处在不必将视窗真的关闭而中断正在浏览的页面,其实这并不是PowerShell提供的功能,只是COM元件的基本使用方式,不过搭配起来颇实用。如果怕太麻烦,PowerShell提供类似「.CMD」「.BAT」的批次档,可将指令集合在「.ps1」档桉中一起执行,或是直接建立一个函式:

Function Hide {$ie.visible=$false;cls}

只要键入「hide」就能关闭视窗加上清除PowerShell的指令记录,可是函数在PowerShell视窗关掉后就会不见,如要将函数永久性保存,可在「X:\Documents and Settings\帐号名称\MY Documents\WindowsPowershell」资料夹下建立一个名为Microsoft.PowerShell_profile的ps1档桉,将函数储存在里面,下次开启PowerShell时就可直接调用,甚至不需要开启PowerShell视窗,直接在XP命令行键入指令即可,如下为启动浏览器搜寻的例子:

function GO($url) {

$ie = New-Object -ComObject InternetExplorer.Application

$ie.navigate(“http://www.google.com.tw/search?btnI=I&q=$url”)

$ie.visible=$true;

}

Profile只是单纯的纯文字档,用记事本编辑就可以了,也可以使用PowerGUI来做,方便载入、储存及语法的标记。

调用了Google的好手气功能后,只需在命令行中输入「powershell go 电脑王」便可直接连到电脑王网站。PowerShell 能直接调用「COM」与「WMI」物件实现对系统的管理,不一定限制于进阶的系统操作,平时的档桉管理便很实用。比如说当拷贝或搬移档桉到随身碟时,常常会遇到容量不足的问题,然而系统并不会提示需要空出多少容量,自己懒得算时删太少不够,又不忍心一次砍太多档桉,就会需要像这样的一个提示工具:

PowerShell基本应用──磁区空间计算

function YEScopy($file,$path) {

copy-item $file $path;

trap [Exception]

{

$file_length=get-item $file | foreach {$_.length};

$disk_space=Get-WMIObject -class Win32_LogicalDisk -Filter "DeviceID='$path'" | foreach {$_.freespace};

$needspace=$file_length - $disk_space;

"磁盘空间不足,还需要 " + $needspace + " bytes/" +([math]::truncate($needspace/1kb+1))+" KB/" + ([math]::truncate($needspace/1mb+1)) +" MB的空间。";

continue;

}

}

在PowerShell视窗内键入「yescopy 档名 目的地」,如果目标磁区容量不足,便会出现提示还需空出多少空间的信息,否则就直接将档桉拷贝过去。

关键字:COM

「Component Object Model」元件组件模型,是一项软体平台,用来实现应用程式与网页伺服端之间的存取,比如说在PHP网页里加入COM物件来操作Word,像是「$wd=New-Object -ComObject Word.Application」便可以启动Word,再叫Word去做其它事情。所以我们也可以用PowerShell来实现即时记事本的功能,在命令行下打一行字,并呼叫COM元件将之存于档桉中。

我们可以直接将PowerShell的指令写入函式中,「copy-item」是PowerShell的内部命令之一,用来拷贝物件,并不限于拷贝档桉,也可以拷贝登录值中的机码,上面的函式先呼叫copy-item来拷贝档桉,如果失败了就用「trap[Exception] {要执行的内容}」这段叙述来侦错,第一步先算出档桉的大小,再用WMIObject取得目标磁区的剩馀空间,利用PowerShell的聪明计算功能将两者相减,再以bytes/KB/MB的格式列出,就不必开小算盘来换算空间大小了。函式中的[math]::truncate等同于VBScript的INT函数,用来取一个数值的整数部份,基本上PowerShell的函式中没有办法直接套用VBScript的语法,如果已经养成了习惯就是改不掉,倒是可以呼叫指令码控制元件:

function VB_MSG($msg) {

$VBS= New-Object -ComObject ScriptControl;

$VBS.Language = "VBScript";

$VBS.AddCode('Function ShowMsg(msg) msgbox (msg):End Function');

$VBS.CodeObject.ShowMsg($msg);

}

PowerShell并没有直接提供如MSGBOX的函数,要弹出对话框除了调用COM物件的wscript.shell外,将VBScript包在函式中也是个可行的办法,先将「VBS」指定为指令码控制物件,把VBScript通通塞进去,最后再呼叫里面的函数即可。

PowerShell基本应用二──火车时刻表查询

综合以上功能,我们可以来做一个从命令行执行的火车时刻表查询。在台铁的火车时刻查询网页中,可以发现上方的网址夹有参数资讯,可将参数拿来跟原始档中的数值做比对,我们所要做的,其实也就是把正确的参数填进网址中罢了。不过台铁的简易版火车时刻表是使用选项清单来选择车站,选项真正的值是是一串四位数的代号而不是站名,直接将中文字填进网址中行不通,所以必须先到原始档中找每个车站的代号。

function Train($source,$target,$date,$from_time,$to_time) {

$OutputEncoding = [Console]::OutputEncoding

$wc = new-Object System.Net.WebClient;

$wc.Encoding = [System.Text.Encoding]::GetEncoding("utf-8");

$wc.DownloadString("http://new.twtraffic.com.tw/nonscript.aspx") > "catch.txt";

当然不是在网页上按右键用人工的方式看原始档,不然这个script就没有意义,我们要先把网页内容抓下来,让指令码去比对。一开始先将变数wc指定为Net.WebClient元件,Net.WebClient元件是用来传送与接收资料的URI,这边用它来实现网页内容的抓取,记得要先把编码格式设为utf-8,不然会没有办法正确显示中文,最后一行则是将台铁查询页的内容抓到「catch.txt」档桉里。指定OutputEncoding的目的则是让待会的findstr函数能正确的抓到中文字串。

$HTML_DATA=get-content "catch.txt";

$Clean_DATA=$HTML_DATA[75..$html_data.count];

$start=$Clean_DATA | findstr /c:$source;

$place=$start[0].indexof($source);

$start=$start[0].substring($place-6,4);

$start;

$end=$Clean_DATA | findstr /c:$target;

$place=$end[0].indexof($target);

$end=$end[0].substring($place-6,4);

$end;

关键字:WMI

「Windows Management Instrumentation」,是微软订出来用script方便管理系统的一种方式,每个WMI物件可视为一串集合,并有自己的名称,通常会以「Win32_xxxx」的方式存在,如我们就是呼叫了「Win32_LogicalDisk」来看电脑上的磁区内容,当然还可以利用「Win32_BIOS、Win32_Processor」来看BIOS及CPU的资讯。

关键字:URI

「Uniform Resource Identifier」,我们平常上网时的URL(Uniform Resource Identifier Locator),还有跟网路其实没有直接关係的URN(Uniform Resource Identifier)都是URI的子集合,URL用来指定如「http://、ftp://」的网路资源存取,URN则是代表一项资源的名称,如书本的ISBN码、MPEG的集合等。



只要从HTML中找出车站的代码,塞进网址中就能做时刻表的查询。

要让PowerShell呈现图形化介面,倒是有许多简单的方式,如图中的对话框可用更简洁的两行来完成。

Get-content是很好用的命令,相对于以往指令码要读取档桉内容,得用逐行读取或是资料流的方式,这边只需一个命令便可一次把catch.txt的档桉内容全部读出来,将刚才抓取的HTML码放入$HTML_DATA变数中。不知道台铁是为了安全起见或是另有目的,网页前面有好长的一段看似加密过的原始码,这跟我们要做的事不相干,存在着反而会造成待会判别字串时的错误,我们会用到的原始码内容差不多从第75行才开始,因此只取75行之后到结束的HTML码到$Clean_DATA变数中,车站的代码也就在这段范围里。接下来就可以把$Clean_DATA的资料放入管线,找出在命令行里输入的起程站名,大部份的站名都只有一个选项,可是像比较特别的如台北站、高雄站因为是预设值,所以会有两个选项,佔两行空间,不过比对只需要一行就够了,也就是到文字阵列0(第一行)去找出站名在该行中为第几个字元,再往前数六个字元的位置就是四位数的车站代号,不要问为什么是六个字元,开原始档偷看的。中文站名与代号间的字元数通常会固定,然而代号在一行中的绝对位置却不一定,所以使用相对位置的方法来取车站代号至$start与$end变数中。

$ie = New-Object -ComObject InternetExplorer.Application;

$ie.navigate("http://new.twtraffic.com.tw/nonscript_search
_result.aspx?SBeginStation=$start&SBeginStationName=
&SEndStation=$end&SEndStationName=&SClass=0&SDateTime
=$date&STime=$from_time|$to_time&SearchType=0
&SBeginCityCode=&SEndCityCode=&BStation
Index=&EStationIndex=") ;

$ie.visible=$true;

最后同样开启IE,把参数们直接套进网址中便完成,以后要查询火车时刻,直接在命令行打「powershell train 台北 高雄 96/10/26 0700 2359」(起程站、终点站、乘车日期、最早时间、最晚时间)就能连上台铁的查询结果网页。如果想直接在Console Mode下看,也可以再用一次DownloadString来下载执行结果,不过自己要想办法将HTML码除掉,并排列整齐就是了。

小结

以上便是PowerShell的基本应用,只是冰山一角,虽然就基本操作上而言,不致于百分之百比原来的CMD.EXE方便,比如说CMD.EXE用DIR就可以看到的剩馀磁盘容量,到了PowerShell中需要一长串指令如「Get-WmiObject -Class Win32_LogicalDisk」来实现,但是在PowerShell中随时都可以结合指令码来执行更複杂的动作,语法又很好学。不过目前似乎在国外的讨论区上比较热门,还有颇多蛮有趣的应用,相对之下繁体中文的讨论文章不多。如果你觉得平常实在用不到那么多功能,或是想捡现成工具玩,国外的网站上可以找到「PsTools」,是由好几个指行档组合而成的一套管理工具,如直接变更使用者密码,提供较进阶的关机选项,在远端电脑上执行程式等等,全部都是在命令行下执行,有点像是作业系统有时会发佈的扩充资源套件「resource kits」的补充,功能挺实用,可当作懒人包,或直接把档桉丢进Windows资料夹中当作是外部命令的补充。至于script的撰写,还是利用GUI介面比较方便,PowerShell底下要直接修改或除错比较不容易,除了记事本之外,有间「Quest Software」公司为PowerShell量身订做了「PowerGUI」,主要是方便指令码的撰写与管理,只是对中文的支持似乎不是很好。

除了用来查火车时刻外,如果不信任Google的好手气可以带你到正确的网站,也可以自行抓网页内容下来分析搜索结果,自作Google私人客制版。

PowerGUI提指令码的NODE检视,以及系统变数的详细清单,便于指令码的撰写参考。

图中的pspasswd.exe能直接对系统进行帐号的密码管理动作,虽然说在CMD.EXE下也可以用net命令达成。Pstools里虽然有些指令功能很好用,可以当作原本Windows外部指令的扩充,但是有一部份却显得画蛇添足。

Powered by Holeo © 2008 UIFU.com