微軟社區(qū)并沒有我們想象中那么遙不可及,WINDOWS系統(tǒng)遇到問題,可以在第一時間去那里反饋,很快就會有微軟工程師幫助你解決問題。如果采用百度的方法,大概率會遇到各種搬來搬去的答案,而這些答案你并不能確定是不是你想要的。
大部分的網(wǎng)友在使用電腦的過程中,遇到問題的時候,都會習慣性地去百度發(fā)帖求助。比如百度知道、百度貼吧,又或者通過百度搜索找到形形色色的論壇尋找答案。
四天前我曾經(jīng)發(fā)過一個微頭條《Win 10 新版2004 這一波更新問題多多,建議最近關(guān)閉自動更新》,后來發(fā)現(xiàn),只要安裝了適用于 WINDOWS 10 V2004的07累積更新x64系統(tǒng)(kb4565503),就會導致瀏覽器崩潰,無論是微軟新版edge瀏覽器還是360極速瀏覽器,結(jié)果都一樣。網(wǎng)上搜索不到類似的問題,只好先關(guān)閉更新。
今天突然想起來,以前好像去過微軟社區(qū),看過許多技術(shù)貼。不過印象中那些微軟工程師很高冷的樣子。抱著試試看的心態(tài),登錄了微軟社區(qū),發(fā)了一個帖子。
讓我沒想到的是,還不到5分鐘,就收到了通知。
打開頁面一看,是一位姓張的微軟“獨立顧問”回的帖子。反應速度還真快,比百度知道快多了。
按照他的指引,下載了一個免安裝的故障排查工具進行了排查,很快就有了結(jié)果。
接下來我又回到微軟社區(qū),把這些運行檢查的截圖發(fā)在了回復下面。依然是反應速度超快,工程師就出現(xiàn)了。
以后在使用WINDOWS和相關(guān)軟件的過程中,如果遇到問題,最直接、最有效的方法應該是去微軟社區(qū)。雖然頁面打開有些慢(應該是服務(wù)器在美國的緣故),但工程師的反應速度和認真態(tài)度卻是讓人驚訝的。至少,你不必在網(wǎng)上瞎逛,望眼欲穿地等待那些同樣是瞎逛的“高手”;也不必在一大堆復制粘貼的答案中戰(zhàn)戰(zhàn)兢兢地尋找可能會更糟糕的方法去解決問題了。
───────────────
本文系陰山原創(chuàng),轉(zhuǎn)載請注明出處。
如果您從事 IT 行業(yè)已久,您就會意識到有大量數(shù)據(jù)需要管理。每臺計算機都有數(shù)十個有價值的組件和特性。例如,如果一種新病毒僅影響特定版本的Windows 10,那么要準確確定公司中有多少臺計算機處于危險之中,需要緩解和考慮,則可能是一個瘋狂的沖刺。
硬盤空間可能會迅速填滿,一旦硬盤已滿,關(guān)鍵數(shù)據(jù)可能會不可挽回地丟失。隨著 PC 的老化,您可能需要升級 RAM 以保持系統(tǒng)正常運行。序列號通常被供應商用于支持,而對于遠程計算機,在過去,收集所有必需的數(shù)據(jù)充其量意味著一個電話,甚至可能最壞的情況是前往該地點以獲取此信息等等。更糟糕的是,如果您沒有收集所有數(shù)據(jù)或記錄不正確,這可能意味著代價高昂的返工。
如果我們可以簡單地運行一個腳本并從任何計算機、計算機列表(例如特定地理位置的所有 PC)或域上的所有活動計算機收集所有這些信息以及更多信息,那不是很好嗎?這正是我們將在本章中要解決的需求!
數(shù)據(jù)收集代碼如下;像我們的其他腳本一樣,我們將詳細介紹它。有幾個特殊領(lǐng)域需要注意,詳見下文。
Param(
[Parameter(Mandatory=$true)][string]$SavePath,
[Parameter(Mandatory=$false)][string]$LookUpType
)
Function Get-File(){
[System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”)
| Out-Null
$FileDialogBox = New-Object System.Windows.Forms.OpenFileDialog
$FileDialogBox.filter = “txt (*.txt)| *.txt”
$FileDialogBox.ShowDialog() | Out-Null
return $FileDialogBox.filename
}
while ([string]::IsNullOrEmpty($LookUpType)) {
Write-Host "Do you want to query a (s)ingle PC, a (l)ist of PCs,`
or (a)ll PCs on your domain:"
$LookUpType=Read-Host "s, l, a: or any other key to cancel?"
}
switch ($LookUpType[0]) { #A
"a" {$ObjList=(Get-ADComputer -filter *|Where-Object `
{$_.enabled -eq $true}).name }
"s" {$ObjList=Read-Host "Enter the name of the PC" }
"l" {$TargetList=Get-File;
$ObjList=Get-Content $TargetList }
Default {write-host "No valid LookUpType found, please try again and`
select : a, s, or l"; exit}
}
foreach($PC in $ObjList){
$Alive=Test-Connection -Count 1 $PC -Quiet #B
if ($Alive){
$Online="True"
$base= Get-CimInstance -computername $PC -ClassName ` #C
Win32_ComputerSystem -ErrorAction SilentlyContinue
$os = Get-CIMInstance -computername $PC -class `
Win32_OperatingSystem -ErrorAction SilentlyContinue
$vol = Get-CIMInstance -computername $PC -class Win32_Volume|
Where-Object {$_.DriveLetter -eq "C:"}`
-ErrorAction SilentlyContinue
$net = Get-CIMInstance -computername $PC -class `
Win32_NetworkAdapterConfiguration |
where-object { $_.IPAddress -ne $null } -ErrorAction `
SilentlyContinue
}else{
$Online="False"
}
$DeviceInfo= @{ #D
Online=$Online
SystemName=$PC
OperatingSystem=$os.name.split("|")[0]
SerialNumber= $os.SerialNumber
Version=$os.Version
Architecture=$os.OSArchitecture
Organization=$os.Organization
Domain=$base.Domain
RAM_GB=$base.TotalPhysicalMemory/1GB
IPAddress=($net.IPAddress -join (", "))
Subnet=($net.IPSubnet -join (", "))
MACAddress=$net.MACAddress
DiskCapacity_GB= $vol.Capacity/1GB
FreeCapacity_GB=$vol.FreeSpace/1GB
}
while ($SavePath -notlike "*.csv"){
Write-Error "The save path must end in a csv file name `
`n Example: c:\temp\report.csv"
$SavePath=Read-Host "Enter the full path and name of the csv to save`
the data to"
}
$DeviceInfo| Export-CSV $SavePath -Append #E
}
一段時間以來,我們一直在腳本中使用條件邏輯。if、Elseif 和 Else 語句允許我們在腳本中做出決定。這些語句的結(jié)構(gòu)使它們非常適合三個或更少的選擇。
$Fruit=Read-Host "Pick a fruit"
if ($Fruit -eq "banana"){
write-host "a $fruit is yellow"
}elseif ($Fruit -eq "apple") {
write-host "an $fruit is red"
}else{
write-host "I don't know what color a/an $fruit is."
}
但是,當您引入三個以上的選擇時,它會很快變得復雜和丑陋。
$Fruit=Read-Host "Pick a fruit"
if ($Fruit -eq "banana"){
write-host "a $fruit is yellow"
}elseif ($Fruit -eq "apple") {
write-host "an $fruit is red"
}elseif ($Fruit -eq "orange") {
write-host "an $fruit is orange"
}elseif ($Fruit -eq "grape") {
write-host "a $fruit is green"
}elseif ($Fruit -eq "cherry") {
write-host "a $fruit is red"
}elseif ($Fruit -eq “kiwi”) {
write-host “a $fruit is green”
}else{
write-host “I don’t know what color a/an $fruit is.”
}
Switch 語句是一種更簡潔的方法,用于檢查多個條件,而無需嵌套的 if/else 語句。
$Fruit=Read-Host “Pick a fruit”
switch ($Fruit) {
“banana” {write-host “a $fruit is yellow”}
“apple” {write-host “an $fruit is red”}
“orange” {write-host “an $fruit is orange”}
“grape” {write-host “a $fruit is green”}
“cherry” {write-host “a $fruit is red”}
“kiwi” {write-host “a $fruit is green”}
Default {write-host “I don’t know what color a/an $fruit is.”}
}
在 PowerShell 中,使用關(guān)鍵字定義開關(guān),后跟要在括號內(nèi)檢查的表達式。然后將開關(guān)操作封裝在大括號中。switch
在這些大括號中,條件的可能匹配項隨后用引號定義,每個大括號后跟應執(zhí)行的操作 如果該匹配項等于條件的值,則每個操作都由其自己的花括號集定義。
可以在 switch 語句中定義和檢查條件的多個值。檢查的每個后續(xù)值都列在 switch 語句的大括號內(nèi)。然后執(zhí)行計算結(jié)果為 true 的第一個值。
使用 switch 語句時,最佳做法是建立默認操作。當其他條件均未解析為 true 時,將發(fā)生默認操作。
用戶可能是不可預測的;因此,我們可能不確定他們將要鍵入的確切輸入。在我們的Tiny PowerShell項目中,我們正在構(gòu)建一個菜單并要求我們的用戶導航它。
write-host "Shall we continue?"
$question=Read-Host "Yes, No or Go Back"
switch ($question){
"Yes" {write-host "You typed Yes"}
"No" {write-host "You typed No"}
"Go Back" {write-host "You typed Go Back"}
Default {write-host "I didn't understand."}
}
這是一個簡單的菜單示例。它會提示用戶輸入“是”、“否”或“返回”響應。如果它沒有得到它期望的答案,它會告訴用戶它不理解。
但是,如果用戶只是鍵入“Y”會發(fā)生什么?
因為我們的條件不使用通配符,所以開關(guān)會查找條件的完全匹配項(不區(qū)分大小寫)?!笆恰被颉笆恰睍鹱饔?,但正如我們所看到的,“y”不起作用。對于我們的用戶來說,我們菜單的這種行為似乎令人沮喪,因為他們不斷輸入完整的單詞。我們可以通過簡單地檢查響應的第一個字符來解決此問題。這將評估“是”和“y”相同。
write-host "Shall we continue?"
$question=Read-Host "Yes, No or Back"
switch ($question[0]){
"Y" {write-host "You typed Yes"}
"N" {write-host "You typed No"}
"B" {write-host "You typed Back"}
Default {write-host "I didn't understand."}
}
首先,我們創(chuàng)建一個名為 $LookUpType 的變量。我們將其作為腳本的參數(shù)之一,以便用戶可以在初始化腳本后立即包含要包含在清單中的所需數(shù)量的計算機(一臺、多臺或全部)。
但是,如果在初始化腳本時沒有將其作為參數(shù)傳遞,我們設(shè)置了一個 While 循環(huán),只要 $LookUpType 變量為 null 或空,它就會提示我們的用戶提供此信息。
最后,我們使用 switch 語句檢查$LookUpType的值。例如,我們使用 [0] 數(shù)組表示法選擇響應的第一個字符,以防用戶鍵入“all”而不是“a”。$LookUpType變量中返回的值將確定我們從中提取硬件信息的電腦數(shù)量和電腦數(shù)量。
如果用戶輸入“a”或任何以“a”開頭的字符串,腳本將對域中所有已啟用的計算機的名稱執(zhí)行 Active Directory 查詢,并將$ObjList變量設(shè)置為此列表。
如果用戶選擇“s”或任何以“s”開頭的字符串,腳本將提示用戶輸入他們想要檢查的PC的名稱,并將$ObjList變量設(shè)置為等于此值。
如果用戶選擇“l(fā)”或任何以“l(fā)”開頭的字符串,腳本將創(chuàng)建一個變量$TargetList,然后運行 Get-Content 函數(shù)。此功能將打開一個GUI菜單,允許用戶選擇包含PC列表的.txt文件。然后,腳本將讀取此文件的內(nèi)容,并將$ObjList變量設(shè)置為與此內(nèi)容相等。
如果用戶按任何其他鍵或鍵組合,腳本將提示用戶未找到有效的 LookUpType 并退出腳本。
當您大規(guī)模處理對象時,性能成為我們必須特別注意的問題。如果腳本在每個系統(tǒng)或?qū)ο笊线\行需要幾秒鐘,則可以將分鐘、小時甚至幾天添加到足夠大的集合中。通常會增加幾秒鐘的一件事是無法遠程訪問的遠程計算機。PowerShell 將嘗試訪問系統(tǒng)并等待命令超時,然后再繼續(xù)。下面我們可以看到這對腳本運行時的影響
可以運行度量命令 cmdlet 以查看執(zhí)行命令或腳本塊需要多長時間。該 cmdlet 在內(nèi)部運行腳本塊,并計算執(zhí)行所需的時間,并在腳本執(zhí)行后顯示結(jié)果。
這是對腳本進行同類比較的好方法。
在這里,我們將在兩臺遠程計算機上運行一個簡單的獲取內(nèi)容 cmdlet。一臺計算機 PC-01 已打開并響應。另一臺計算機 PC-02 已關(guān)閉,無法遠程訪問。
兩秒鐘似乎并不多。但是,如果您只有 30 臺計算機不可用,這將增加我們完成的總時間一分鐘以上。如果您的企業(yè)擁有數(shù)千或數(shù)萬個系統(tǒng),這可能會為您的請求增加大量時間。
這就是測試連接 cmdlet 可以使我們的生活更輕松的地方。與 ping 一樣,Test-Connection 將互聯(lián)網(wǎng)控制消息協(xié)議 (ICMP) 回顯請求數(shù)據(jù)包發(fā)送到目標或目標列表。這是一種在嘗試遠程訪問系統(tǒng)或其資源之前查看系統(tǒng)是否聯(lián)機且可訪問的快速方法。
您可以使用 count 參數(shù)控制發(fā)送和接收的請求數(shù)。
您可能想知道,“我不能用ping完成所有這些操作嗎?為什么要使用測試連接?原因是,測試連接也可以使用 quiet 參數(shù)根據(jù) echo 請求的結(jié)果返回布爾值。對于傳統(tǒng)的 ping 請求,您需要根據(jù)響應執(zhí)行其他條件邏輯,以確定 ping 是成功還是失敗。此函數(shù)內(nèi)置于測試連接 cmdlet 中,不需要額外的條件邏輯或開銷。
測試連接 cmdlet 的語法包括幾個有用的參數(shù):
例如,以下語句將向主計算機發(fā)送單個 ICMP 回顯請求,也稱為環(huán)回測試。
Test-Connection -count 1 -TargetName $env:COMPUTERNAME
當我們在圖 10.9 中運行的相同代碼中插入測試連接時,我們看到執(zhí)行時間有所縮短。
在腳本中建立要查詢的計算機列表后,我們使用 foreach 循環(huán)來查詢每臺計算機的硬件屬性。但是,由于我們不想浪費時間查詢無法應答的電腦(特別是因為我們需要多次查詢每個框),因此我們首先使用測試連接 cmdlet 向計算機發(fā)送單個 ICMP 回顯請求,以查看它是否響應。
我們使用 cmdlet 的 -Quiet 參數(shù),如果響應響應,則僅返回 True 響應,如果無法訪問,則返回 False。然后,我們設(shè)置一個 if 語句來檢查此響應的值,如果值為 True,則查詢系統(tǒng)的硬件。
如果無法訪問 ICMP 回顯請求,我們可以繼續(xù)不執(zhí)行任何操作。相反,我們包含此腳本的“Else”部分,如果值為 false,我們只需將調(diào)用$Online的變量設(shè)置為 False,然后再移動到列表中的下一臺計算機。我們之所以包含此步驟,是因為我們將在報表中將其用作值。當我們交付報告時,如果讀者可以立即看到運行查詢時系統(tǒng)未聯(lián)機,則阻止了許多關(guān)于為什么字段為空的問題。
計算機存儲大量數(shù)據(jù),其中大量數(shù)據(jù)與計算機本身有關(guān)。這包括文件權(quán)限、注冊表和有關(guān)其硬件的信息,這對我們來說是完美的!訪問此數(shù)據(jù)很容易,可以使用PowerShell和Get-CimInstance cmdlet遠程完成。
CIM 代表公共信息模型,是一種面向?qū)ο蟮拈_源標準,用于訪問和管理各種計算機、網(wǎng)絡(luò)、服務(wù)和應用程序信息。自1996年以來,Microsoft一直將其用作其Windows管理規(guī)范(WMI)的基礎(chǔ)。然而,隨著PowerShell Core及更高版本從單一平臺(僅Microsoft)到多平臺(Microsoft,MacOS,Linux...)支持的轉(zhuǎn)變,CIM是訪問這一信息寶庫的方式。
在 CIM 結(jié)構(gòu)中訪問數(shù)據(jù)時,了解數(shù)據(jù)的分組方式非常重要。例如,您將無法從與其操作系統(tǒng)版本相同的位置訪問遠程系統(tǒng)的 IP 地址。此信息被組織到稱為類的數(shù)據(jù)結(jié)構(gòu)中。
類是數(shù)據(jù)的結(jié)構(gòu)方式。這樣可以將相似的東西分組,這樣你就不需要解析計算機正在跟蹤的數(shù)千個東西來找到你需要的東西。但是,由于這種分組,您必須知道需要查詢哪個類才能獲取所需的數(shù)據(jù)。
可以通過運行 Get_CimClass cmdlet 來獲取系統(tǒng)知道的可用 CIM 類的列表。
Get-CimClass
運行該命令后,您開始理解我所說的計算機存儲大量有關(guān)自身的數(shù)據(jù)是什么意思!花一點時間挖掘這些類,看看你可能會發(fā)現(xiàn)什么樣的隱藏信息寶石,這可能是一個有趣的時間利用。
通常,將 CIM 類搜索限制為 Win32 也可能是一個好主意。通常,這些類保存了對系統(tǒng)管理員最有價值的信息。
Get-CimClass |Where Get-CimClass|Where-Object {$_. CimClassName -match "Win32"}
但是知道我們的系統(tǒng)上有哪些職業(yè)可用只是戰(zhàn)斗的一部分。我們?nèi)匀恍枰廊绾卧L問這些數(shù)據(jù)。這就是 Get-CimInstance cmdlet 的用武之地。
有幾個參數(shù)用于 Get-CimInstance cmdlet:
Get-CimInstance -ClassName Win32_OperatingSystem -Property Version|Format-Table
Get-CimInstance -ClassName Win32_OperatingSystem|Format-Table
還可以使用點表示法拉取存儲在 CIM 類中的特定屬性。這允許我們拉取完整的類,將其存儲為變量,然后使用 Dot 表示法訪問類的特定方面,而無需不斷重新查詢 CIM 實例。
確定可以使用測試連接 cmdlet 在網(wǎng)絡(luò)上訪問計算機后,我們通過 Get-CimInstance cmdlet 查詢遠程電腦,存儲來自以下位置的類信息:
一旦我們將四個 CIM 實例的值存儲在變量中,我們將 CIM 類的特定部分拉到其他變量中用于我們的哈希表(您將在下一節(jié)中了解哈希表)。使用這四個 CIM 類,我們可以檢索以下各項的值:
哈希表只是一種數(shù)據(jù)存儲結(jié)構(gòu),如變量或數(shù)組。每個數(shù)據(jù)項都存儲在鍵/值對中,有時稱為字典值。鍵/值對,很簡單,是一個與另一個值配對的唯一字符串。如果我們想創(chuàng)建一對總裁和他們的副總裁,我們的鍵/值對可能如下所示:
副總統(tǒng)
“喬治華盛頓”
“約翰·亞當斯”
“約翰·亞當斯”
“托馬斯·杰斐遜”
“托馬斯·杰斐遜”
“亞倫伯爾”
要創(chuàng)建哈希表,首先使用前導 $ 定義哈希表的名稱,并將其設(shè)置為等于 @{}。這與數(shù)組的結(jié)構(gòu)類似,您可以在數(shù)組中創(chuàng)建數(shù)組名稱,后跟 @()。這不是我們將在哈希表和數(shù)組之間看到的唯一相似之處,但我們也將探討一些差異。以下是我們?nèi)绾蝿?chuàng)建和顯示我們的副總裁哈希表:
$VicePresidents=@{
'George Washington'="John Adams"
'John Adams'="Thomas Jefferson"
'Thomas Jefferson'="Aaron Burr"
}
$VicePresidents
關(guān)于副總統(tǒng)的哈希表,您可能會注意到的一件事是,數(shù)據(jù)的存儲順序與我們提供的順序不同。我們的第一個鍵/值對是“喬治華盛頓”和“約翰亞當斯”。但是當我們查詢哈希表時,第一個條目是“約翰·亞當斯”“托馬斯·杰斐遜”。這是因為哈希表使用自己的算法決定將鍵/值條目放置在后端數(shù)組中。使用此算法排列數(shù)據(jù)可以非??焖俚卣业教囟ǖ逆I/值對。
假設(shè)我們想知道托馬斯·杰斐遜的副總統(tǒng)。我們可以簡單地使用方括號中的鍵查詢數(shù)組。
$VicePresidents['Thomas Jefferson']
同樣,語法與訪問數(shù)組非常相似。但與訪問數(shù)組不同,我們不需要事先知道數(shù)據(jù)是在哪個位置輸入的。哈希表的算法可以為我們找到它,比我們搜索數(shù)組要快得多。
讓我們使用這些信息設(shè)置一個哈希表和一個數(shù)組:
$VicePresidentsHashtable=@{
'George Washington'="John Adams"
'John Adams'="Thomas Jefferson"
'Thomas Jefferson'="Aaron Burr"
}
$VicePresidentsArray=@("John Adams", "Thomas Jefferson", "Aaron Burr")
使用 Measure-Command,我們可以看到在數(shù)組中找到 Aaron Burr 需要多長時間。
Measure-Command -Expression {$VicePresidentsHashtable['Thomas Jefferson']}
Measure-Command -Expression {$VicePresidentsArray[2]}
如果您還不知道要在陣列中拉取的數(shù)據(jù)的位置,則這種影響甚至更大。假設(shè)您想從哈希表和數(shù)組中拉出 Aaron Burr。若要從哈希表訪問答案,只需知道鍵,但要從數(shù)組訪問答案,需要查看所有條目,并使用 Where-Object cmdlet 選擇所需的條目。
Measure-Command -Expression {$VicePresidentsHashtable['Thomas Jefferson']}
Measure-Command -Expression {$VicePresidentsArray|Where-Object {$_ -eq "Aaron Burr"}}
從PC中提取CIM類數(shù)據(jù)后,我們創(chuàng)建一個名為$DeviceInfo的哈希表。然后,我們使用包含我們存儲的類的四個變量為我們關(guān)心的每個硬件類別創(chuàng)建鍵/值對,然后再將整個鍵和值表導出到我們的 CSV。
在前面的章節(jié)中,我們已經(jīng)探討了CSV(逗號分隔值)數(shù)據(jù)格式的一些強大用途。例如,此數(shù)據(jù)類型可以由文本編輯器和Microsoft Excel讀取,這使其非常適合報表。但在前面的章節(jié)中,我們導入了 CSV 文件以在腳本中使用?,F(xiàn)在我們從哈希表中獲得了這個結(jié)構(gòu)非常出色的數(shù)據(jù),您可能想知道,“我們?nèi)绾卧诒A羝浣Y(jié)構(gòu)的同時保存這些數(shù)據(jù)?這就是導出 CSV cmdlet 的用武之地。
我們現(xiàn)在非常舒服地將數(shù)據(jù)寫出到輸出、主機甚至文本文件中。幸運的是,導出 CSV cmdlet 非常簡單,我們只需要非常注意幾個選項。
從 CIM 類構(gòu)造哈希表后,我們調(diào)用哈希文件的內(nèi)容,然后使用 -path 參數(shù)將內(nèi)容傳送到 Export-CSV cmdlet 中,以指向腳本的 $SavePath 參數(shù)。我們還使用 -append 標志將當前哈希表鍵/值添加到 CSV 中。這是因為我們正在執(zhí)行 Get-CimInstance 并在 foreach 循環(huán)中創(chuàng)建哈希表。我們希望確保將每臺 PC 的 CIM 類和哈希表的數(shù)據(jù)記錄到報告中,然后繼續(xù)下一個,確保我們?yōu)?Objlist中的每臺 PC 提取適當?shù)挠布畔?。此外?Append 標志非常重要。如果沒有此標志,循環(huán)中的最新數(shù)據(jù)將寫入文件,列表中的下一項將覆蓋此文件,下一項將覆蓋該文件。使用 -append 標志,循環(huán)的每次迭代都會寫入其數(shù)據(jù),下一次迭代將其數(shù)據(jù)追加到列表中。
但是,由于 path 參數(shù)正在查找.csv文件,因此我們必須確保$SavePath包含.csv文件類型。因此,在寫入我們的csv之前,我們設(shè)置了一個防護裝置,以確保$SavePath滿足我們的格式需求。
我們設(shè)置了一個 while 循環(huán)來檢查$SavePath的值,雖然此變量中的值不包含.csv擴展名,但它會提示并重新提示我們的用戶使用以.csv文件擴展名結(jié)尾的文件重新輸入保存路徑。