zl程序教程

您现在的位置是:首页 >  其他

当前栏目

3.PS编程入门基础语法

基础编程入门 语法 PS
2023-06-13 09:13:35 时间

[TOC]

0x00 PS 对象类(Object-Class)

描述:我们在学习C++和Java/PHP都遇到过面向对象编程,同样在PS也是基于对象来运行的脚本语言;

简单的说 对象=属性+方法 组成:

(1) 属性可以描述一个对象例如一把小刀拥有一些特殊的属性(颜色、制造商等),对象的属性可以被PS自动转换成文本,并且输出到控制;

只读属性:一个构造器中只有Get方法,没有Set方法

读写属性:一个构造器中只有Get/Set方法

#(0)查看你对象的属性
$Host | Get-Member -MemberType Property 

#(1)输出的第一列为对象的属性,第二列为文本形式的属性值
PS > $host.Version
Major  Minor  Build  Revision  # 包含 Major,Minor,Build,Revision四个属性
-----  -----  -----  --------
2      0      -1     -1        # 值Value
PS > $host.Version.Major  #通过对象属性里面的对象进行调用
5
PS > [System.Version]'2019.12.20.1234' #可以通过这个类型构造新的对象或者进行类型转换
Major  Minor  Build  Revision
-----  -----  -----  --------
2019   12     20     1234


#(2)$host的CurrentCulture访问当前系统的本地化信息和该信息的类型:
PS > $Host.CurrentCulture
LCID             Name             DisplayName
----             ----             -----------
2052             zh-CN            中文(中华人民共和国)
#通过MSDN查看System.Globalization.CultureInfo的构造函数可知,可以将国家代码和国家名称标志字符串转换成一个新的CultureInfo对象。
PS > [System.Globalization.CultureInfo]'zh-tw'
LCID             Name             DisplayName
----             ----             -----------
1028             zh-TW            中文(繁体,中国台湾)

(2) 方法定义了一个对象可以做什么事情例如打开计算机,当你把一个对象输出在控制台时它的属性可能会被转换成可视的文本,但是它的方法却不可见

  • 标准方法:几乎每个对象都有一些继承自父类的方法,这些方法并不是该对象所特有的方法,而是所有对象共有的方法。

Equals 比较两个对象是否相同

GetHashCode 返回一个对象的数字格式的指纹

GetType 返回一个对象的数据类型

ToString 将一个对象转换成可读的字符串

# (0) 查看对象的方法
PS > $Host | Get-Member -MemberType Method

# Get_ 和 Set_ 方法
PS > $Host.get_Version()
# Major  Minor  Build  Revision
# -----  -----  -----  --------
# 5      1      18362  145
$Host.UI.WriteDebugLine("Hello 2012 !")  #实用的调用输出调试(而不像输出错误的信息)

# (1) 采用.net对象的静态方法实用,使用Parse方法将一个字符串转换成DateTime类:
[System.DateTime]::Parse("2012-10-13 23:42:55")
  # 2012年10月13日 23:42:55

Tips : Powershell将信息存储在对象中,每个对象都会有一个具体的类型;(例如简单的文本会以System.String类型存储,日期会以System.DateTime类型存储。)

Tips : 任何.NET对象都可以通过GetType()方法返回它的类型,该类型中有一个FullName属性,可以查看类型的完整名称(例如$date.GetType().FullName)。

1.操作对象

描述: 通过New-Object可以创建一个对象,可以通过Add-member添加对象属性,通过下面示例也能学习到调用属性和方法,并且删除我们创建的对象。

基础实例:

#(1) New-Object可以创建一个对象,空对象什么都没有,如果调用它不会返回任何东西。
PS > $obj = New-Object object  #空对象
PS > $obj 
  # System.Object

#(2) 增加对象属性 
PS > Add-Member -MemberType NoteProperty -InputObject $obj -Name Color -Value "Red"
PS > Add-Member -MemberType NoteProperty -InputObject $obj -Name Name -Value "WeiyiGeek"
PS > $obj
  # Color Name
  # ----- ----
  # Red   WeiyiGeek

#(3) 增加方法 -memberType 选项使用ScriptMethod。
PS > Add-member -MemberType ScriptMethod -InputObject $obj script {"I'm is function, $obj.name "}
PS > Add-Member -memberType ScriptMethod -InputObject $obj -Name fun -Value { "I'm whittling now" }
PS > $obj | Add-Member ScriptMethod corkscrew { "Pop! Cheers!" } #直接通过管道增加一个新方法

#(4)调用属性和方法
PS > $obj | get-Member  #获取对象的属性和方法
PS > $obj
PS > $obj.Name
  # WeiyiGeek
PS > $obj.script()
  # I'm is function, System.Object.name'
PS > $obj.fun()
  # I'm whittling now
PS > $pocketknife.corkscrew()
  # Pop! Cheers!
PS > $pocketknife.corkscrew # 在调用方法时如果没有使用圆括号,方法不会执行但是可以返回方法的基本信息。
  # Script                      :  "Pop! Cheers!"
  # OverloadDefinitions : {System.Object corkscrew();}
  # MemberType           : ScriptMethod
  # TypeNameOfValue  : System.Object
  # Value                      : System.Object corkscrew();
  # Name                     : corkscrew
  # IsInstance               : True

#(5)对象删除调用对象的Delete方法:
PS > -Path $obj
  # True
PS > $obj.Delete()
PS > -Path $obj
  # False

2.对象属性

描述:一个对象的属性用来存储数据,反过来这些数据又可以存储其它对象。例如$host有两个比较特别的属性UI和PrivateData

基础示例:

# (1) 属性中包含对象
# 把$host对象输出到控制台上后,除了UI和PrivateData所有的属性都会被转换成确定的文本:
$Host
  # Name		: ConsoleHost
  # Version         	: 2.0
  # InstanceId       	: 7fefa1fa-fb2e-47c7-a867-c13b123da5c2
  # UI               	: System.Management.Automation.Internal.Host.InternalHostUserInterface
  # PrivateData      	: Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy

# (2) 属性UI和PrivateData这两个属性中又包含了一个对象:
# “RawUI” 为 “Raw User Interface” 提供了配置Powershell控制台用户界面的接口。
PS C:Powershell> $Host.UI
  # RawUI
  # -----
  # System.Management.Automation.Internal.Host.InternalHostRawUserInterface

PS C:Powershell> $Host.UI.RawUI  # 属性可以读取但是个别却不能更改。
  # ForegroundColor	: DarkYellow
  # BackgroundColor	: DarkMagenta


# (2) 只读属性和读写属性
# - 通过简单地修改控制台的背景和前景的颜色,可以发现属性更改可以直接反映到对象上。
$host.ui.rawui.ForegroundColor = "White"

# - KeyAvailable 有的属性不能更改,如果尝试修改就会抛出异常。
PS C:Powershell> $Host.UI.RawUI.KeyAvailable
  # False
PS C:Powershell> $Host.UI.RawUI.KeyAvailable=$false
  # “KeyAvailable”为 ReadOnly 属性。
  # 控制台是否接收到了一个按键请求,应当取决于用户的操作,因此该属性拒绝被更改,你只能读取它。


# (3) 特殊对象属性对象静态属性和动态属性
# 一个NoteProperty包含了`静态的数据`,一个ScriptProperty中`包含了一段脚本`,通过脚本计算出属性的值;
PS > $obj=New-Object PSobject
# 静态 () 小括号 | # 动态 {} 代码块
PS > $obj | Add-Member -MemberType NoteProperty -Name AddTime -Value (get-date)       
PS > $obj | Add-Member -MemberType ScriptProperty -Name CurrentTime -Value {get-date} 

PS > $obj
  # # CurrentTime属性会自动更新、AddTime则不会。
  # AddTime                 CurrentTime
  # -------                 -----------
  # 2012/1/11 14:35:38      2012/1/11 14:36:35  # 每次执行会进行改变!
PS C:\Users\WeiyiGeek> $obj.AddTime
  # 2021年4月27日 15:54:42
PS C:\Users\WeiyiGeek> $obj.CurrentTime
  # 2021年4月27日 15:57:04

3.对象方法

描述:每一个类型都可以包含一些静态的方法,可以通过方括号和类型名称得到类型对象本身;

基础示例:

# (1) 动态方法
$Host.GetType()
  # IsPublic IsSerial Name                                     BaseType
  # -------- -------- ----                                     --------
  # False    False    InternalHost                             System.Management.Automation.Host.PSHost

# (2) 静态方法
[System.DateTime] | Get-Member -static -memberType *Method
  # #System.DateTime类支持的静态方法非常实用使用Parse方法将一个字符串转换成DateTime类:
  # [System.DateTime]::Parse("2012-10-13 23:42:55")
  # [System.DateTime]::IsLeapYear(1988) #IsLeapYear方法判断闰年


# (3) 一个方法的多种参数,通过Definition属性查看函数定义
$method=$Host.UI | Get-Member WriteLine
$method.Definition
  # System.Void WriteLine(), System.Void WriteLine(System.ConsoleColor foregroundColor, System.ConsoleColor backgroundColor
  # , string value), System.Void WriteLine(string value)
$method.Definition.Replace("),",")`n") # 换行输出

Tips : 必须知道这个方法的功能,因为有的命令可能比较危险,例如错误地修改环境变量。

4.对象实例

描述: 可以采用类似于Java声明实例化对象类的流程来实现创建对象。

示例1.每一个Powershell命令都会返回一个对象,但是返回的对象不易操作(自动将对象转换成为可视化字符串)

# 1.存储在$FileList变量中的并不是真实的对象,而是一个对象数组,数组可以通过索引访问得到真实的对象。
$FileList=dir
$obj=(dir)[0]
  # 目录: C:\Users\WeiyiGeek
  # Mode                 LastWriteTime         Length Name
  # ----                 -------------         ------ ----
  # d-----         2019/7/26      8:45                .android

# 2.使用对象的属性,如果属性的定义列中包含{get;set}表明该属性可以被更新:
$obj.LastAccessTime
# 2021年4月26日 6:43:51
$obj.LastAccessTime=Get-Date
2021年4月28日 14:21:26

示例2.如果使用构造函数创建一个指定类型的实例对象,该类型必须至少包含一个签名相匹配的构造函数。

# 例如-可以通过字符和数字创建一个包含指定个数字符的字符串:
New-Object String('*',100)
  # ***********...**************

Q:为什么支持上面的方法?

答: 原因是String类中包含一个 Void .ctor(Char, Int32) 构造函数 查看命令: [String].GetConstructors() | foreach {$_.tostring()}

示例3.通过类型转换创建对象即(通过类型转换可以替代New-Object)

# 将字符串转成时间类型
[DateTime]$date="1999-9-1 10:23:44"
$date.GetType().FullName
System.DateTime

# 如果条件允许也可以直接将对象转换成数组
[char[]]"weiyigeek"
  # w
  # e
  # i
  # y
  # i
  # g
  # e
  # e
  # k

# 将字符转成ASCII码
[int[]][char[]]"weiyigeek"
  # 119
  # 101
  # 105
  # 121
  # 105
  # 103
  # 101
  # 101
  # 107

示例3.使用.net对象类型进行实例创建

$LocalName="c:\PS\Index.php"
$DownUrlFile="https://weiyigeek.com/Index.php"

# 方式1.脚本脚本写法
if(!-Path $LocalName){
  $webClient=New-Object Net.WebClient;$webClient.DownloadFile($DownUrlFile,$LocalName)
  if(-Path $LocalName){
    Write-Ouput "下载完成"
  }
}

# 方式2.命令格式写法
(New-Object Net.WebClient).DownloadFile($DownUrlFile,$LocalName)

5.加载DLL程序集

描述: 在Powershell中加载这个自定义C#类库编译生成的dll,并使用其中的Student类的构造函数生成一个实例,最后调用ToString()方法。

Step 1.自定义一个简单的C#类库编译为Test.dll:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
namespace Test
{
    public class Student
    {
        public string Name { set; get; }
        public int Age { set; get; }
        public Student(string name, int age)
        {
            this.Name = name;
            this.Age = age;
        }
        public override string ToString()
        {
            return string.Format("Name={0};Age={1}", this.Name,this.Age);
        }
    }
}

Step 2.在PS中加载Test.dll到系统

PS > $TestDLL=ls .Test.dll
PS > [reflection.assembly]::LoadFile($TestDLL.FullName)  #加载DLL到PS中
  # GAC    Version        Location
  # ---    -------        --------
  # False  v2.0.50727     Test.dll

Step 3.使用其中的Student类的构造函数生成一个实例,最后调用ToString()方法。

PS > $stu=New-Object Test.Student('Mosser',22)  #创建对象
PS > $stu
  # Name   Age
  # ----   ---
  # Mosser  22
PS > $stu.ToString()  #调用重写的ToString方法
  # Name=Mosser;Age=22

6.使用COM对象

描述: 作为.NET对象的补充Powershell可以加载和访问COM对象。

Tips : 常用的COM对象中有:

  • WScript.Shell,
  • WScript.Network,
  • Scripting.FileSystemObject,
  • InternetExplorer.Application,
  • Word.Application,
  • Shell.Application

Step 1.每一个COM对象都有存储在注册表中的唯一标识符,想遍历访问可用的COM对象,可是直接访问注册表。

# (1) 显示前10条COM对象的ProgID
Get-ChildItem REGISTRY::HKEY_CLASSES_ROOT\CLSID -include PROGID -recurse | foreach {$_.GetValue("")} | select -First 10
  # file
  # StaticMetafile
  # StaticDib
  # clsid
  # objref
  # ADODB.Command.6.0
  # ADODB.Parameter.6.0
  # ADODB.Connection.6.0
  # ADODB.Recordset.6.0
  # ADODB.Error.6.0
  # DAO.PrivateDBEngine.36


# (2) 查找指定的COM对象的唯一标识符
Get-ChildItem REGISTRY::HKEY_CLASSES_ROOT\CLSID -include PROGID -recurse | foreach {$_.GetValue("")} | Select-String "WScript"
  # WScript.Network.1
  # WScript.Shell.1
  # WScript.Shell.1
  # WScript.Network.1

Step 2.怎样使用COM对象得到了COM对象的ProgID,就可以使用使用New-Object创建COM对象查看其

# 注意: 需要指定参数为 -comObject
PS > $WS=New-Object -ComObject WScript.Network.1
PS > $WS | Get-Member -me *method               # 查看方法
PS C:\Users\WeiyiGeek> $WS.EnumNetworkDrives()  # 网络
  # Z:
  # \\10.10.17.106\Work
PS C:\Users\WeiyiGeek> $WS.EnumPrinterConnections() # 打印机
  # Kingsoft Virtual Printer Port
  # 导出为WPS PDF
  # WpsPrinter:
  # 发送到WPS高级打印
  # Brother MFC-3880N Printer
PS > $WS | Get-Member -me *property # 查看属性
PS C:\Users\WeiyiGeek> $WS.UserDomain
  # WEIYIGEEK
PS C:\Users\WeiyiGeek> $WS.UserName
  # WeiyiGeek

Step3.使用WScript.shell COM对象和它的方法CreateShortcut()做桌面上创建一个Powershell快捷方式:

PS> $shell=New-Object -ComObject WScript.shell
PS> $link=$shell.CreateShortcut("$path/Powershell.lnk")  
PS> $link.Description="启动Powershell"
PS> $link.TargetPath='Powershell.exe'
PS> $link.IconLocation='Powershell.exe'
PS> $link.WorkingDirectory=$PROFILE
PS> $link.Save()
PS> $link
  # FullName         : C:\Users\WeiyiGeek\Desktop\Powershell.lnk
  # Arguments        :
  # Description      : 启动Powershell
  # Hotkey           :
  # IconLocation     : Powershell.exe,0
  # RelativePath     :
  # TargetPath       : C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe
  # WindowStyle      : 1
  # WorkingDirectory : C:\Users\WeiyiGeek\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
# 查看相关属性
$link | get-member
TypeName:System.__ComObject#{f935dc23-1cf0-11d0-adb9-00c04fd58a0b} 
TargetPath       Property   string TargetPath () {get} {set}
#Get
$link.TargetPath()
#Set
$link.TargetPath="cmd.exe"

Tips : COM对象的和.NET对象相似,所以可是使用Get-Member得到该对象的所有熟悉和方法:


0x01 PS 脚本(Script)

描述: 做过开发的人都知道写得代码量越大其可读性就越差,解决的最好方式就是在写脚本时融入函数(Function)类库(Class)的概念。

  • 函数:把实现一些小功能的代码写成一个函数,不仅可以增强代码的可读性,还可以很方便的重用。一旦你创建了一个实现特定功能的函数,也可以下次在其它脚本中使用。
  • 类库:把需要的函数嵌入进类库中,就不用每次在执行脚本时拷贝函数,并且还可以在需要时扩充它。另外以函数的方式构建类库,还可以让你更专注特定功能的具体实现,降低脚本开发的复杂度。

基础示例:

# 1) 在脚本中使用函数,本示例为通过Factorial函数求阶乘。
# Tips: Powershell中的函数必须先定义后使用。
# MyScript.ps1
param([int]$n=$(throw "请输入一个正整数"))
Function Factorial([int]$n)
{
  $total=1
  for($i=1;$i -le $n;$i++)
  {
    $total*=$i
  }
  return $total
}
Factorial $n

PS C:\Users\WeiyiGeek> .\test.ps1
  # 请输入一个正整数
  # 所在位置 \test.ps1:1 字符: 17
PS C:\Users\WeiyiGeek> .\test.ps1 3
6

# 2) 将脚本分为工作脚本和类库, 可以便于浏览增加可读性并且可以隔离含税使它不容易被修改;
# 将上述的代码中Factorial函数保存在PSLib.ps1之中。
param([int]$n=$(throw "请输入一个正整数"))
# fa
. .PSLib.ps1   
# 方式2 采用 source .PSLib.ps1
Factorial $n
PS E:> .MyScript.ps1 10 # 执行脚本
  # 3628800

Tips :脚本在执行时先加载类库中的函数(加载函数类库和执行脚本类似), 只需要在前面增加一个句号中间有空格。

Tips: 我们可以将类库脚本集中存放常常有两种方式, 但是最好在当前用户的私人目录中存放脚本相对来说比较安全:

  • 1.一种方法是和工作脚本存放在一起可以使用相对路径。
  • 2.一种方法是分开存放,加载时就得使用绝对路径了。
PS C:\Users\WeiyiGeek> cd $env:APPDATA
PS C:\Users\WeiyiGeek\AppData\Roaming> cd $env:APPDATA\PSLib
PS C:\Users\WeiyiGeek>copy .PSLib.ps1 $env:APPDATA\PSLib

脚本参数

描述: 在Powershell的脚本中,可以使用自定义的参数列表,通常将参数放在Param()

基础语法&参数:

# 基础语法
[Cmdletbinding()]
param(
[Parameter(Mandatory=$True)]
[Alias('DT')]
[ValidateSet(2,3)]
[int]DiskType
[Boolean]
)

# 参数说明
Mandatory 设置为True表示该参数强制输入
Alias 是为该参数设置了一个别名
ValidateSet 是为该参数设置了一个可用值列表
[数据类型] 指定该变量的类型

基础示例:

# scriptArg.ps1
[Cmdletbinding()]
param(
[Parameter(Mandatory=$True)][String]$Name,
[Alias('DT')]$AliasDt,
[ValidateSet(1,2,8,9)]$Scope,
[ValidateSet("My","Love","Computer")]$ScopeStr,
[Int]$DiskType,
[Boolean]$Flag
)

Write-Host $Name $DiskType $Flag
Write-Host $AliasDt $DT
Write-Host $Scope $ScopeStr

# 脚本执行 & 结果
.\ScriptArgs.ps1 -Name "WeiyiGeek" -AliasDt "PowerShell" -DiskType 1024 -Flag 0 -Scope 1 -ScopeStr Computer
  # WeiyiGeek 1024 False
  # PowerShell 
  # 1 Computer

Tips : 同时在脚本中使用write-verbose输出详细信息,在运行脚本的时候可以使用-verbose就能输出相关信息

脚本选择

描述: 这里使用host.UI.PromptForChoice()方法进行功能的实现,可以先看看方法的定义host.ui.PromptForChoice;

简单示例:

# (1) 方式1
# 参数定义(先定义后使用)
$SwitchUser = ([System.Management.Automation.Host.ChoiceDescription]"&Switchuser")
$LoginOff = ([System.Management.Automation.Host.ChoiceDescription]"&LoginOff")
$Lock = ([System.Management.Automation.Host.ChoiceDescription]"&Lock")
$Reboot = ([System.Management.Automation.Host.ChoiceDescription]"&Reboot")
$Sleep = ([System.Management.Automation.Host.ChoiceDescription]"&Sleep")

# 将上述值装入到数组之中
$Selection = [System.Management.Automation.Host.ChoiceDescription[]]($SwitchUser,$LoginOff,$Lock,$Reboot,$Sleep)

# 选择菜单设置(1点数组中第二的一个值)
$Answer=$Host.UI.PromptForChoice("接下来做什么事呢?",'请选择:' ,$selection ,1)
Write-Host -NoNewline "您选择的是:"
switch($Answer)
{
  0 {"切换用户"}
  1 {"注销"}
  2 {"锁定"}
  3 {"重启"}
  4 {"休眠"}
  default {"正在完成操作请稍等!"}
}

执行结果:

接下来做什么事呢?
请选择:
[S] Switchuser  [L] LoginOff  [L] Lock  [R] Reboot  [S] Sleep  [?] 帮助 (默认值为“L”): S
您选择的是:切换用户

脚本异常处理


使用Write-Debug有两个优势,首先调试信息会自动高亮显示,便于分析。其次,这些调试信息只会在调试模式开启时输出,控制起来更加方便。当然最重要的是这些临时信息无论什么时候也不会混淆在返回值。

抑制错误信息 函数中的错误信息,也有可能作为返回值的一部分,因为默认这些错误信息会直接输出。 27 Function ErrorTest() {

#该进程不存在
Stop-Process -Name "www.mossfly.com"

} ErrorTest

Stop-Process : 找不到名为“www.mossfly.com”的进程。请验证该进程名称,然后再次调用 cmdlet。 所在位置 C:UsersbaozhenDesktoptest.ps1:6 字符: 17

  • Stop-Process <<<< -Name “www.mossfly.com"
    • CategoryInfo : ObjectNotFound: (www.mossfly.com:String) [Stop-P rocess], ProcessCommandException
    • FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell. Commands.StopProcessCommand

    很明显,类似这样的错误提示信息,对调试程序很重要,但如果你觉得它不重要,特意要隐藏,可以使用$ErrorActionPreference进行设置。 Function ErrorTest() { #从这里开始隐藏所有的错误信息 $ErrorActionPreference=”SilentlyContinue” Stop-Process -Name “www.mossfly.com" #该进程不存在 }

#错误信息不会输出 ErrorTest 但是上面的做法并不明智,因为这样可能错过其它错误提示。所以最好的方式是处理完后,对$ErrorActionPreference进行复位。 Function ErrorTest() {

#从这里开始隐藏所有的错误信息
$ErrorActionPreference="SilentlyContinue"
Stop-Process -Name "www.mossfly.com"
#该进程不存在

#恢复$ErrorActionPreference,错误开始输出
$ErrorActionPreference="Continue"

2/0

} ErrorTest 试图除以零。

所在位置 行:9 字符: 7

  • 2/ <<<< 0
  • CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
  • FullyQualifiedErrorId : RuntimeException

PowerShell: Try…Catch…Finally 实现方法 https://www.cnblogs.com/digjim/archive/2012/06/22/2558458.html

Powershell错误处理,try catch finally https://www.cnblogs.com/micro-chen/p/5941660.html