好像關于Powershell說的已經差不多了,所以最后一篇文章就來使用Powershell寫一些腳本,幫助我們完成一些日常工作。
文件管理
常用命令
先來看看常用的文件管理命令。
Set-Location
命令用于切換工作目錄,它的別名是cd
。
Get-Location
命令用于獲取當前工作目錄,它的別名是pwd
。
Get-ChildItem
命令用于獲取當前目錄下的所有文件。
Get-Item
命令用于獲取給定文件的信息。
還有文件移動、刪除、復制、粘貼、重命名等命令,輸入Get-Command -Noun item
就可以看到這些命令,這里就不做介紹了。
獲取文件信息
獲取文件信息可以利用命令Get-Item
。下面獲取了我電腦上的cmder.exe
可執行文件的信息。
λ Get-Item .\Cmder.exe
目錄: D:\devtools\cmder_mini
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2016/12/2 7:15 130560 Cmder.exe
默認只列出這么三個屬性,當然其實文件屬性遠不止這些。我們可以通過管道,將文件信息對象傳遞給命令Select-Object
,讓它幫我們顯示所有屬性。這里只粘貼了一點點內容,其實文件信息很長,大家可以自行嘗試。
λ Get-Item .\Cmder.exe|Select-Object *
PSPath : Microsoft.PowerShell.Core\FileSystem::D:\devtools\cmder_mini\Cmder.exe
PSParentPath : Microsoft.PowerShell.Core\FileSystem::D:\devtools\cmder_mini
過濾文件
用Get-ChildItem
顯示當前當前文件的時候,會顯示所有文件。有時候我們可能僅僅需要搜索或者過濾部分文件。
首先,如果是比較簡單的需求,可以使用?*
通配符來搞定,問號用于匹配任意單個字符,星號用于匹配任意多個字符。比方說,我想要列出所有.md
格式的文件,就可以使用下面的命令。
PS D:\devtools\cmder_mini> Get-ChildItem *.md
目錄: D:\devtools\cmder_mini
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2016/12/2 7:14 73491 CHANGELOG.md
-a---- 2016/12/2 7:14 1784 CONTRIBUTING.md
-a---- 2016/12/2 7:14 10039 README.md
有時候可能需要使用正則表達式來查找文件,不過好像Get-ChildItem
沒有正則表達式查詢的命令行,不過我們可以使用Where-Object
命令來自定義查詢。如果了解C#語言的LINQ的話,應該可以猜到,這個命令對應于LINQ的where
語句。
下面同樣是查找所有.md
格式的文件,不過這次使用了Where-Object
和正則表達式,其中Where-Object
里面的$_
是形式變量,代表每次迭代的文件。如果了解過C#的LINQ,或者Java 8的流類庫,應該對這種形式會比較熟悉。
Get-ChildItem|Where-Object {$_ -match '\w*.md$'}
如果僅僅為了搜索文件名的話,這種方式好像一點優勢都沒有。實際上Where-Object
的功能非常強大。比方說,我現在想查找大于5kb
的所有.md
格式文件,那么就可以這么寫。這里又用到了Powershell的一個方便的特性,文件大小單位,KB GB MB TB
等單位都支持。當然其實并不僅僅可以查詢文件大小屬性,基本上所有文件信息都可以用來查詢。
Get-ChildItem|Where-Object {$_ -match '\w*.md$' -and $_.Length/1kb -gt 5}
最后,Get-ChildItem
不僅可以列出當前文件夾下的所有內容,還可以遞歸查詢所有子文件夾。比方說,我要查找一下迅雷文件夾下所有可執行文件,就可以使用下面的命令。如果添加-Depth
參數的話,還可以指定遞歸深度。
Get-ChildItem -Recurse *.exe
修改hosts
訪問谷歌的一種方式就是更改hosts文件。這里就用Powershell做一個修改hosts的功能。
首先先來介紹一個命令Invoke-WebRequest
,利用它我們可以獲取網頁內容、下載文件甚至是填寫表單。這個命令的別名是iwr
、curl
和wget
。我們就使用它來下載網上的hosts文件。
剩余就沒有什么難度了,無非就是讀寫文件、追加文件、復制和粘貼這種基本操作。最后寫完這個功能發現有一百多行,就不往這里復制粘貼了。如果有興趣的話,可以直接看我的Github上面的腳本。
進程管理
查看進程
首先我們看看有多少和進程相關的命令,這個很簡單,只要查看一下名詞是Process
的命令即可。
PS C:\WINDOWS\system32> Get-Command -Noun process
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Debug-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Get-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Start-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Stop-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Wait-Process 3.1.0.0 Microsoft.PowerShell.Management
使用這些命令,我們就可以非常方便的管理進程了。比方說,我想查詢現在運行的所有進程,就可以使用下面的命令,這樣就會列出所有運行的進程,就像任務管理器里顯示的那樣。
PS C:\WINDOWS\system32> Get-ProcessGet-Process
上面這個命令會顯示所有進程。如果需要,我們可以按照某個屬性對進程進行排序顯示,這需要使用另外一個命令Sort-Object
。另外,如果只需要顯示前幾個進程,可以使用命令Select-Object
來選擇顯示多少數據。比方說,如果我們要查看當前占用CPU前5的chrome進程,就可以使用下面的命令。
Get-Process chrome|Sort-Object cpu -Descending|Select-Object -First 5
利用這幾個命令,我們可以按照任何想要的方式來查詢進程。
管理進程
先來看看MSDN上的一個官方例子。首先先打開三個記事本進程,然后使用名稱獲取這些進程,然后調用進程的Kill()
函數即可把這些進程全殺掉。中間調用了Count
屬性測試了一下總共獲取到了幾個進程。
PS C:\WINDOWS\system32> notepad;notepad;notepad;
PS C:\WINDOWS\system32> $notepads=Get-Process -Name notepad
PS C:\WINDOWS\system32> $notepads.Count
3
PS C:\WINDOWS\system32> $notepads.Kill()
再學習Powershell編程的時候,我們常常會同時開幾個Powershell窗口。不再使用的時候一個一個關閉它們也是一件麻煩事情,所以官方文檔還為我們介紹了如何關閉除當前窗口外的所有Powershell進程。
每個Powershell進程都有一個變量$PID
,用于標志當前進程的進程號,利用這一點我們就可以實現這個功能。這里的-WhatIf
參數表示不真正關閉進程,僅列出將要關閉的進程。
PS C:\WINDOWS\system32> Get-Process powershell |Where-Object {$_.Id -ne $PID}|Stop-Process -WhatIf
WhatIf: 正在目標“powershell (2676)”上執行操作“Stop-Process”。
如果既想要關閉進程,還想知道關閉了哪些進程,可以使用-PassThru
參數。
PS C:\WINDOWS\system32> Get-Process powershell |Where-Object {$_.Id -ne $PID}|Stop-Process -PassThru
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
573 30 56884 68992 0.92 2676 1 powershell
輪詢關閉進程
如果在死循環中不斷查找任務管理器進程,發現它在運行就把它關閉,就可以做一個小小的“病毒”。代碼很簡單,基本上一下子就能看懂。一開始我沒有加Sleep,然后CPU使用率飚的非常高,加了之后基本上對電腦性能沒有影響了。
$process_name = "taskmgr"
while ($true) {
$processes = Get-Process
if ($processes.Name -contains $process_name) {
Get-Process $process_name|Stop-Process
}
else {
Start-Sleep -Milliseconds 500
}
}
如果把上面代碼中的taskmgr
換成英雄聯盟的進程名字,我們就可以做一個簡單的“熊孩子防火墻”,防止熊孩子用電腦來玩游戲了。
注冊表操作
讀寫注冊表
讀取注冊表
首先來介紹一下注冊表根的簡寫,例如HKEY_CURRENT_USER
的簡寫就是HKCU
,HKEY_LOCAL_MACHINE
的簡寫就是HKLM
。知道了簡寫,我們就可以將Powershell的工作目錄切換到注冊表內。例如,如果我們想查看HKEY_CURRENT_USER\Control Panel\Desktop\MuiCached
下的值,就可以先把工作目錄切換到這個位置內部。這里需要將對應的注冊表根修改為對應的簡寫加冒號的形式。
PS C:\WINDOWS\system32> Set-Location 'HKCU:\Control Panel\Desktop\MuiCached'
PS HKCU:\Control Panel\Desktop\MuiCached>
切換到了注冊表內部,我們就可以利用Get-Item
命令獲取注冊表的值了。比如說,要獲取這個注冊表鍵的值,就可以直接輸入Get-Item .
了。注意這個點不能省去,它代表當前工作目錄。
PS HKCU:\Control Panel\Desktop\MuiCached> Get-Item .
Hive: HKEY_CURRENT_USER\Control Panel\Desktop
Name Property
---- --------
MuiCached MachinePreferredUILanguages : {zh-CN}
如果要獲取當前注冊表項的屬性值,可以利用Get-ItemProperty
命令。
PS HKCU:\Control Panel\Desktop\MuiCached> Get-ItemProperty . MachinePreferredUILanguages
MachinePreferredUILanguages : {zh-CN}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Control Panel\Desktop\MuiCached
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Control Panel\Desktop
PSChildName : MuiCached
PSDrive : HKCU
PSProvider : Microsoft.PowerShell.Core\Registry
當然,切換工作目錄這件事情也可以不做。直接利用Get-ItemProperty
命令通過路徑參數來獲取屬性。
Get-ItemProperty -Path 'HKCU:\Control Panel\Desktop\MuiCached' -Name MachinePreferredUILanguages
編輯注冊表項
下面這個路徑是一個安全的注冊表路徑,在這里修改注冊表不會造成嚴重的系統問題。所以我們把它保存為一個變量。
$path = "HKCU:\Control Panel\Desktop"
如果要新建注冊表項,可以使用New-Item
命令。我們可以使用注冊表編輯器regedit
來驗證項是否創建成功。
New-Item –Path $path –Name HelloKey
如果要修改項的屬性,使用Set-ItemProperty
命令。
Set-ItemProperty -path $path\hellokey -name Fake -Value fuck
最后,如果要刪除項的屬性,使用Remove-ItemProperty
命令。
Remove-ItemProperty -path $path\hellokey -name Fake
如果要刪除整個注冊表項,使用Remove-Item
命令。
Remove-Item -path $path\hellokey -Recurse
獲取當前.NET版本
下面的參考資料中列出了一個MSDN上的文檔,告訴我們如何讀取注冊表的值來判斷當前安裝了.NET Framework的版本,并給出了相應的C#代碼。下面的代碼做的就是將C#代碼改寫成Powershell腳本。
# 判斷系統當前安裝.NET框架版本的腳本
$path = 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full'
$not_found_msg = ".net framework 4.5 or later not installed on your system"
function CheckFor45PlusVersion([int] $releaseKey) {
if ($releaseKey -ge 460798) {
return "4.7 or later";
}
if ($releaseKey -ge 394802) {
return "4.6.2";
}
if ($releaseKey -ge 394254) {
return "4.6.1";
}
if ($releaseKey -ge 393295) {
return "4.6";
}
if (($releaseKey -ge 379893)) {
return "4.5.2";
}
if (($releaseKey -ge 378675)) {
return "4.5.1";
}
if (($releaseKey -ge 378389)) {
return "4.5";
}
return $not_found_msg;
}
try {
$key = get-item $path
$releaseKey = Get-ItemPropertyValue $path 'release'
if ($releaseKey -is [int]) {
$releaseKey = [int]$releaseKey
$version = CheckFor45PlusVersion($releaseKey)
Write-Host ".NET framework ${version}"
}
else {
Write-Host $not_found_msg
}
}catch {
Write-Host $not_found_msg
}
Office互操作
操作Excel
雖然Powershell可以通過COM接口和Office程序交互,不過最常用的還是操作Excel,所以我這里只介紹如何控制Excel表。下面最后一個參考資料就是我參考的操作Excel的文章,可能需要梯子才能訪問。需要注意一點,既然是操作Excel,當然首先電腦上需要先安裝Excel才能正常使用。
打開和關閉
首先,我們來創建一個Excel對象,這樣實際上會創建一個Excel應用程序。
$excel = New-Object -ComObject Excel.Application
執行了上面的命令,什么事情都沒有發生。這是因為默認啟動的實例是隱藏的,要顯示Excel的窗口的話,將它設置為可見即可。
$excel.Visible=$true
如果要打開一個現成的工作簿,使用Open
函數。
$workbook = $excel.Workbooks.Open("XXX.xlsx")
如果要創建一個新的工作簿,使用Add
函數。
$workbook = $excel.Workbooks.Add()
一個工作簿可以有多個工作表,要選擇某一個工作表,使用Worksheets.Item
屬性,需要注意這里的下標從一開始。
$worksheet = $workbook.Worksheets.Item(1)
對數據完成操作之后,需要保存的話,使用SaveAs
函數。
$workbook.SaveAs("D:\Desktop\hello.xlsx")
操作數據
前面只說了打開和關閉操作,下面來看看如何具體讀取和寫入數據。首先回到上面那步工作表,因為如果要操作數據,需要在工作表對象上進行操作。
$worksheet = $workbook.Worksheets.Item(1)
要操作數據,調用工作表對象的Cells
屬性即可。比方說我要打印九九乘法表,那么就可以用類似下面的代碼來迭代寫入數據。
for ($i = 1; $i -le 9; ++$i) {
# 第一行
$worksheet.Cells(1, $i + 1) = $i
# 第一列
$worksheet.Cells($i + 1, 1) = $i
# 它們的乘積
for ($j = 1; $j -le 9; ++$j) {
$worksheet.Cells($i + 1, $j + 1) = $i * $j
}
}
操作之后,Excel表中應該存在如圖所示的數據。
類似的,讀取數據也很簡單,只要讀取Cells
屬性即可。
for ($i = 1; $i -le 10; ++$i) {
for ($j = 1; $j -le 10; ++$j) {
Write-Host -NoNewline $worksheet.Cells($i, $j).Text "`t"
}
Write-Host
}
上面的代碼獲取了我們剛才寫入Excel的數據,然后將其轉換為文本并輸出,每個數據之間使用制表符\t
分隔,注意Powershell中的轉義字符使用的這個特殊字符。結果應該類似如圖所示。
繪制圖表
Excel很常用的一種操作就是繪制圖表,這里也簡單說說。不過由于這種資料在網上面實在太少,我就算用谷歌搜索英文網頁也搜不出來多少資料,大部分都屬于一點小腳本。所以這里只能隨便說說了。
首先準備一下數據,我準備了如圖所示的數據。
然后來創建一個圖表對象。如果使用交互式環境Powershell ISE的話,智能提示會顯示這里有AddChart
和AddChart2
兩個方法,不過我看了下文檔,前面那個過時了,所以這里使用帶2的那個版本。
$chart=$worksheet.Shapes.AddChart2().Chart
創建了圖表對象之后,我們為它指定數據源。
$chart.SetSourceData($worksheet.Range('a1', 'd5'))
這時候如果觀察Excel的話,會發現已經出現了一個初步的圖形。如果希望改變圖形樣式的話,設置圖標的類型即可。這里將圖表類型保存為一個變量,之后就可以省略長長的類名了。這里推薦使用Powershell ISE,因為自動補全可以顯示所有類型的圖標,只需要修改一下圖表類型并觀察Excel中圖標類型的變化就可以明白類型和圖標的對應關系了。
$chartTypes = [Microsoft.Office.Interop.Excel.XLChartType]
$chart.ChartType = $chartTypes::xlColumnClustered
最后效果如圖所示,我看到人家可以使用ApplyDataLabels
方法顯示圖例。但是我使用這個方法卻不知道為什么顯示不了。所以這里只能將就一下了。
最后再來畫個餅狀圖,數據還是上面的數據,不過這次只使用語文那一列的數據。基本上和上面的一樣,只有類型那里改成xlPie
。
$chartTypes = [Microsoft.Office.Interop.Excel.XLChartType]
$chart = $worksheet.Shapes.AddChart2().Chart
$chart.SetSourceData($worksheet.Range('a1', 'b5'))
$chart.ChartType = $chartTypes::xlPie
$chart.ApplyDataLabels(5)
最終效果圖如下。
ImportExcel
上面的方法好像只能在安裝Excel的環境下運行,如果沒有安裝Office,但是也想使用編程功能,可以使用第三方的模塊。這就是這里要介紹的ImportExcel。使用它,我們可以在沒有安裝Excel的情況下編輯Excel文件。
首先需要安裝它,可以利用Powershell的包管理器方便的安裝。
Install-Module ImportExcel -scope CurrentUser
如果想讓所有用戶都可以使用這個模塊,需要安裝到全局位置,不過這需要管理員權限,所以需要在管理員模式的Powershell窗口中運行。
Install-Module ImportExcel
這個模塊如何使用我就不作介紹了,這個項目的README文件上基本列出了所有功能和對應的GIF圖,需要什么功能只要看一看應該就可以使用了。
參考資料
https://4sysops.com/archives/interacting-with-the-registry-in-powershell/
https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.aspx
http://www.lazywinadmin.com/2014/03/powershell-read-excel-file-using-com.html