• PowerShell 基础


    PowerShell 基础


    PowerShell 是一种跨平台的自动化任务解决方案,由命令行shell、脚本语言和配置管理框架组成。PowerShell可以在 Windows、Linux 和 macOS 上运行。

    PowerShell 是新式的命令shell,包括其他常用 shell 的基本功能,且能接受并返回.NET对象。可通过函数、类、脚本和模块进行扩展,同时对常用数据格式提供内置支持(如 CSV, JSON, XML )

    PowerShell 命令称为 cmdlet(读作 command-let)。 除了 cmdlet 外,使用 PowerShell 还可以在系统上运行任何可用命令。cmdlet 是本机 PowerShell 命令,而不是独立的可执行文件。 cmdlet 收集在 PowerShell 模块中,可按需加载。 可以用任何编译的 .NET 语言或 PowerShell 脚本语言本身来编写 cmdlet

    1. 参考资料

    2. Visual Studio Code 集成 PowerShell

    • 在VSCode中安装 PowerShell 扩展
      1
    • 全局设置中增加配置
    {
        ...
        "[powershell]": {
            "files.encoding": "utf8bom",
            "files.autoGuessEncoding": true,
            "editor.renderWhitespace": "all",
            "editor.renderControlCharacters": true,
            "files.trimTrailingWhitespace": true
        },
        "powershell.integratedConsole.suppressStartupBanner": true,
        "powershell.integratedConsole.showOnStartup": true,
        "powershell.integratedConsole.focusConsoleOnExecute": true,
        "powershell.debugging.createTemporaryIntegratedConsole": true
    }
    
    • 重启 VSCode,新建或打开 .ps1 文件输入脚本,就可以打断点调试了。

    3. 变量

    在powershell中变量名均是以美元符 $ 开始,剩余字符可以是数字、字母、下划线的任意字符,并且powershell变量名大小写不敏感($a$A 是同一个变量)。

    某些特殊的字符在powershell中有特殊的用途,一般不推荐使用这些字符作为变量名。当然你硬要使用,请把整个变量名后缀用 花括号 ${变量名} 括起来。

    赋值操作符为 =,几乎可以把任何数据赋值给一个变量,甚至一条 cmdlet 命令。

    变量的作用域

    - `$global` 全局变量,在所有的作用域中有效,如果你在脚本或者函数中设置了全局变量,即使脚本和函数都运行结束,这个变量也任然有效。
    - `$script` 脚本变量,只会在脚本内部有效,包括脚本中的函数,一旦脚本运行结束,这个变量就会被回收。
    - `$private` 私有变量,只会在当前作用域有效,不能贯穿到其他作用域。
    - `$local` 默认变量,可以省略修饰符,在当前作用域有效,其它作用域只对它有只读权限。
    

    定义变量

    $a=10
    $b=4
    $msg="保存文本"
    
    # 使用花括号将特殊的变量名包起来
    ${"I"like $}="mossfly"
    
    # 获取当前目录中所有文件或文件夹信息,存入变量 $item 中
    $item=Get-ChildItem .
    
    # 变量写保护
    New-Variable num -Value 100 -Force -Option readonly
    
    # 声明常量
    new-variable num -Value "strong" -Option constant
    
    # 声明带描述的变量, 在New-Variable 可以通过-description 添加变量描述,但是变量描述默认不会显示,可以通过Format-List 查看。
    new-variable name -Value "me" -Description "This is my name"
    
    # 强类型变量
    [byte]$b=101
    
    # 使用 XML 类型查询xml
    [XML]$xml=(Get-Content .xxxx.xml)
    $xml.Note.Note
    
    

    文本

    使用引号可以定义字符串,如果想让自己定义的字符串原样输出,可以使用单引号。

    $text='$fei $(tai) $env:windir 大白博客 (20+2012)'
    $site="飞苔博客 Powershell博客"
    $text="$site $(get-date) $env:windir"
    

    文本中的特殊字符

    "系统目录位于:$env:windir"
    "默认安装程序目录位于:$env:ProgramFiles"
    "机器名为:$env:computername"
     
    "当前日期:$(get-date)"
    "1GB=$(1gb/1kb)KB"
    

    转义字符

    
    | 转义字符 | 描述 |
    | --- | --- |
    | `n | 换行符 |
    | `r | 回车符 |
    | `t | 制表符 |
    | `a | 响铃符 |
    | `b | 退格符 |
    | `' | 单引号 |
    | `" | 双引号 |
    | `0 | Null |
    | ` | 反引号本身 |
    
    # 使用单引号闭合字符串输出双引号
    'The site of my blog is"www.mossfly.com"'
    # 使用转义字符输出双引号
    “My blog site is`"www.mossfly.com`""
    # 在字符串中输出换行符
    “The site of my blog is `"www.mossfly.com`",`n大白博客"
    

    定义多行文本

    这里要注意开始和结束的标记必须另起一行。

    @"
    这首诗用来评价陶渊明的诗歌再好不过了
     
    一语天然万古新,豪华落尽见真淳。
    南窗白日羲皇上,未害渊明是晋人。
    "@
    

    用户交互

    如果要提示用户输入可以使用 read-hostread-host 不会自动解析变量。,不过可以通过ExpandString方法解析。

    # 示例1
    PS E:> $name=Read-Host "请输入您的用户名"
    请输入您的用户名: Mosser Lee
    PS E:> "您输入的用户名为:$name"
    您输入的用户名为:Mosser Lee
    
    # 示例2
    # 通过ExpandString方法解析
    PS E:> $inputPath=Read-Host "请输入文件路径"
    请输入文件路径: $env:windir
    PS E:> $inputPath
    $env:windir
    PS E:> $ExecutionContext.InvokeCommand.ExpandString($inputPath)
    C:windows
    

    接受敏感数据

    PS E:> $pwd=Read-Host -AsSecureString "请输入密码"
    请输入密码: ******
    PS E:> $pwd
    System.Security.SecureString
    # 将加密文本变成普通文本
    PS E:> [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd))
    abc123
    

    询问用户名和密码

    使用 Get-Credential 命令,该命令会弹出一个安全对话框,一旦用户输入完毕,就会返回一个Credential对象包含用户名和密码。

    $cre=Get-Credential myUserName
    $pwd=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($cre.Password))
    "$($cre.UserName), 您的密码: $pwd"
    

    字符串处理

    参考:

    格式化
    "{0} diskettes per CD" -f (720mb/1.44mb)
    500 diskettes per CD
    
    # 使用小写字母d:格式化
    "Date: {0:d}" -f (Get-Date)
    Date: 2013/5/31
    
    # 使用大写字母D:格式化
    "Date: {0:D}" -f (Get-Date)
    Date: 2013年5月31
    
    # 固定列宽格式化输出
    # 将一个逗号放置在通配符与列宽编号的中间,负数设置右对齐,正数设置左对齐。
    dir | ForEach-Object { "{0,-20} = {1,10} Bytes" -f $_.name, $_.Length }
    
    # 打印出日期格式各种格式化效果
    $date= Get-Date
    Foreach ($format in "d","D","f","F","g","G","m","r","s","t","T", `
    "u","U","y","dddd, MMMM dd yyyy","M/yy","dd-MM-yy") {
    "PowerShell 日期格式, 使用 $format : {0}" -f $date.ToString($format) }
    
    生成 GUID
    PS > $guid = [GUID]::NewGUID()
    PS > Foreach ($format in "N","D","B","P") {
    >> "GUID with $format : {0}" -f $GUID.ToString($format)}
    >>
    GUID with N : e1a5d98f4227470b84c2b37a6a8fb894
    GUID with D : e1a5d98f-4227-470b-84c2-b37a6a8fb894
    GUID with B : {e1a5d98f-4227-470b-84c2-b37a6a8fb894}
    GUID with P : (e1a5d98f-4227-470b-84c2-b37a6a8fb894)
    
    替换成目标文本

    -replace 操作符有三种实现方式,其它文本操作符也类似地有三种实现方式,像 -replace-ireplace-creplace,i前缀表示字符串大小写不敏感,c前缀表示字符串大小写敏感。

    # 大小写不敏感
    "Hello Carl" -replace "Carl", "Eddie"
    Hello Eddie
    
    # 大小写敏感
    "Hello Carl" -creplace "carl", "eddie"
    Hello Carl
    
    合并字符串
    'Power' + 'Shell'
    
    验证是否相等
    # 大小写不敏感
    "Power" -eq "power"
    # 大小写敏感
    "Power" -ceq "power"
    
    验证包含关系
    "PsTips.Net" -like "p*"
    
    验证不包含关系
    "PoserShell" -notlike "ps"
    # 模式匹配, 大小写敏感
    "PoserShell" -cnotlike "PO*"
    
    验证模式匹配
    "PoserShell" -match "P*"
    # 大小写敏感
    "Hello" -cmatch "[ao]"
    # 验证不匹配
    "Hello" -notmatch "[ao]"
    # 验证不匹配, 大小写敏感
    "Hello" -cnotmatch "[ao]"
    

    数组

    
    # 数组
    $nums1=2,0,1,2
    $nums2=1..5
    
    # 多态数组
    $array=1,"2012世界末日",([System.Guid]::NewGuid()),(get-date)
    
    # 空数组
    $a=@()
    
    # 单元素数组
    $a=,"moss"
    
    # 数组大小
    $a.Count
    
    # 访问数组
    $array[0]
    $array[($array.Count-1)]
    
    # 从数组中选择多个元素
    $result=ls
    $result[0,3,5,12]
    
    # 数组添加
    $books="元素1","元素2","元素3"
    $books+="元素4"
    
    # 删除数组元素
    $num=1..4
    $num=$num[0..1]+$num[3]
    
    # 复制数组引用
    $chs=@("A","B","C")
    $chsBak=$chs
    
    # 复制数组内容
    $chsNew=$chs.Clone()
    
    # 判断数组是否相等
    $chs.Equals($chsBak)
    
    # 强类型数组
    [int[]] $nums=@()
    
    

    哈希表

    $stu=@{ Name = "小明";Age="12";sex="男" }
    
    # 读取
    $stu["Name"]
    
    # 总数
    $stu.Count
    
    # keys
    $stu.Keys
    
    # values
    $stu.Values
    
    # 在哈希表中存储数组
    $stu=@{ Name = "小明";Age="12";sex="男";Books="三国演义","围城","哈姆雷特" }
    
    # 在哈希表中插入新的键值
    $Student=@{}
    $Student.Name="令狐冲"
    $Student.School="华山派"
    
    # 更新
    $stu.Name="赵强"
    
    # 删除
    $stu.Remove("Name")
    
    

    计算变量

    $result=$a*$b
    

    PowerShell也能自动识别计算机容量单位,包括KB,MB,GB,TB,PB

    # 假如一个网站每个页面大小为80kb,统计显示每天的PV操作为800,1个月下来占用的带宽
    $value=80kb*800*30/1gb
    # 假如一个网站的每天人均PV操作为5,页面大小为80Kb,主机提供商限制的总流量为10G,那平均每天的最大访客数为
    $value=10GB/(80KB*5)/30
    

    同时给多个变量赋值

    $a=$b=$c=123
    

    交换变量的值

    $Value1 = 10
    $Value2 = 20
    $value1,$value2=$value2,$value1
    

    查看正在使用的变量

    # 简写
    ls variable:
    # 完整写法
    Get-ChildItem variable:
    # 查找变量: 查询以 value 打头的变量名
    ls variable:value*
    

    验证变量是否存在

    验证一个变量是否存在,仍然可以象验证文件系统那样,使用cmdlet Test-Path。因为变量存在变量驱动器中。
    
    Test-Path variable:value1
    

    判断变量是否是数组

    "abac" -is [array]
    
    $str="字符串"
    $str.ToCharArray() -is [array]
    

    删除变量

    # 删除变量 value1
     del variable:value1
    

    在控制台中输出变量

    $result
    $msg
    
    # 数组逆序输出
    $books="元素1","元素2","元素3"
    $books[($books.Count)..0]
    
    # 使用哈希表格式化输出
    Dir | Format-Table
    Dir | Format-Table FullName,Mode
    
    $column1 = @{expression="Name"; width=30;label="filename"; alignment="left"}
    $column2 = @{expression="LastWriteTime"; width=40;label="last modification"; alignment="right"}
    ls | Format-Table $column1, $column2
    
    

    对象

    # 创建对象
    $obj=New-Object object
    
    # 增加属性
    Add-Member -InputObject $obj -Name Color -Value "Red"
    Add-Member -InputObject $obj -Name Weight -Value "55"
    $obj | Add-Member NoteProperty Blades 3
    $obj | Add-Member NoteProperty Manufacturer ABC
    
    # 增加新方法
    Add-Member -memberType ScriptMethod -In $obj -name cut -Value { "I'm whittling now" }
    # 指定参数类型增加一个新方法
    Add-Member -in $obj ScriptMethod screw { "Phew...it's in!" }
    # 直接通过管道增加一个新方法
    $obj | Add-Member ScriptMethod corkscrew { "Pop! Cheers!" }
    
    # 使用方法
    $obj.cut()
    $obj.screw()
    $obj.corkscrew()
    
    # 通过Get-Memeber命令查看对象类型支持的所有静态方法
    [System.DateTime] | Get-Member -static -memberType *Method
    
    # 通过类型转换创建日期对象
    [DateTime]$date="1999-9-1 10:23:44"
    
    

    加载C#程序集

    自定义一个简单的 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);
            }
        }
    }
    

    在Powershell中加载这个dll并使用其中的Student类的构造函数生成一个实例,最后调用ToString()方法。

    # 加载dll
    $TestDLL=ls .Test.dll
    [reflection.assembly]::LoadFile($TestDLL.FullName)
    
    # 创建对象
    $stu=New-Object Test.Student('Mosser',22)
    $stu.ToString()
    

    COM 对象

    • 查看可用的COM对象
      每一个COM对象都有存储在注册表中的唯一标识符,想遍历访问可用的COM对象,可是直接访问注册表。
    Dir REGISTRY::HKEY_CLASSES_ROOTCLSID  -include PROGID -recurse | foreach {$_.GetValue("")}
    DAO.DBEngine.36
    DAO.PrivateDBEngine.36
    DAO.TableDef.36
    DAO.Field.36
    DAO.Index.36
    PS C:Powershell> Dir REGISTRY::HKEY_CLASSES_ROOTCLSID -include PROGID -recurse
    | foreach {$_.GetValue("")} | select -First 10
    DAO.DBEngine.36
    DAO.PrivateDBEngine.36
    DAO.TableDef.36
    DAO.Field.36
    DAO.Index.36
    DAO.Group.36
    DAO.User.36
    DAO.QueryDef.36
    DAO.Relation.36
    file
    ......
    
    • 使用 COM 对象
      一旦得到了COM对象的ProgID,就可以使用New-Object创建COM对象,只需要指定参数为 -comObject
    PS C:Powershell> New-Object -ComObject DAO.Relation.36
    
    Properties     : System.__ComObject
    Name           :
    Table          :
    ForeignTable   :
    Attributes     : 0
    Fields         : System.__ComObject
    PartialReplica :
    
    • 获得对象的属性和方法
      COM对象的和.NET对象相似,任然可是使用Get-Member 得到该对象的所有熟悉和方法
    $DBEng=New-Object -ComObject DAO.PrivateDBEngine.36
    $DBEng | Get-Member -me *method
    $DBEng | Get-Member -me *property
    
    • 常用的COM对象
      • WScript.Shell
      • WScript.Network
      • Scripting.FileSystemObject
      • InternetExplorer.Application
      • Word.Application
      • Shell.Application
    # 使用WScript.shell COM对象和它的方法CreateShortcut()做桌面上创建一个Powershell快捷方式
    $wshell=New-Object -ComObject WScript.shell
    $path=[environment]::GetFolderPath('Desktop')
    $link=$wshell.CreateShortcut(“$path/Powershell.lnk”)
    $link | Get-Member
    $link.TargetPath='Powershell.exe'
    $link.Description="启动Powershell"
    $link.WorkingDirectory=$PROFILE
    $link.IconLocation='Powershell.exe'
    $link.Save()
    

    自动化变量

    Powershell 自动化变量 是那些一旦打开Powershell就会自动加载的变量。
    这些变量一般存放的内容包括:

    1. 用户信息:例如用户的根目录 $home
    2. 配置信息: 例如powershell控制台的大小,颜色,背景等。
    3. 运行时信息:例如一个函数由谁调用,一个脚本运行的目录等。

    可以通过 Get-Help about_Automatic_variables 查看自动化变量的帮助信息

    PS > Get-Help about_Automatic_variables
    
    Name                           Valueiables
    ----                           -----~~~~~~
    ?                              Truet_Automatic_variables in a help file in this session. To download updated help topics type: "Update-Help". To get help online, search for the help
    ^                              rary at https://go.microsoft.com/fwlink/?LinkID=107116.
    $
    args                           {}
    ConfirmPreference              High
    DebugPreference                SilentlyContinue
    EnabledExperimentalFeatures    {}
    Error                          {Get-Help could not find about_Automatic_variables in a help file in this session. To download updated help topics type: "Update-Help". To get help online, searc…
    ErrorActionPreference          Continue
    ErrorView                      ConciseView
    ExecutionContext               System.Management.Automation.EngineIntrinsics
    ...
    
    详细说明
    • $$ 包含会话所收到的最后一行中的最后一个令牌。

    • $? 包含最后一个操作的执行状态。如果最后一个操作成功,则包含 TRUE,失败则包含 FALSE。

    • $^ 包含会话所收到的最后一行中的第一个令牌。

    • $_ 包含管道对象中的当前对象。在对管道中的每个对象或所选对象执行操作的命令中,可以使用此变量。

    • $Args 包含由未声明参数和/或传递给函数、脚本或脚本块的参数值组成的数组。在创建函数时可以声明参数,方法是使用 param 关键字或在函数名称后添加以圆括号括起、逗号分隔的参数列表。

    • $ConsoleFileName 包含在会话中最近使用的控制台文件 (.psc1) 的路径。在通过 PSConsoleFile 参数启动 Windows PowerShell 或使用 Export-Console cmdlet 将管理单元名称导出到控制台文件时,将填充此变量。在使用不带参数的 Export-Console cmdlet 时,它自动更新在会话中最近使用的控制台文件。可以使用此自动变量确定要更新的文件。

    • $Error 包含错误对象的数组,这些对象表示最近的一些错误。最近的错误是该数组中的第一个错误对象 ($Error[0])。

    • $Event 包含一个 PSEventArgs 对象,该对象表示一个正在被处理的事件。此变量的值是 Get-Event cmdlet 返回的同一个对象。因此,可以在 Action 脚本块中使用 $Event 变量的属性(例如 Event.TimeGenerated)

    • $EventSubscriber 包含一个 PSEventSubscriber 对象,该对象表示正在被处理的事件的事件订阅者。
      此变量只在事件注册命令的 Action 块内填充。此变量的值
      是 Get-EventSubscriber cmdlet 返回的同一个对象。

    • $ExecutionContext 包含一个 EngineIntrinsics 对象,该对象表示 Windows PowerShell 主机的执行上下文。可以使用此变量来查找可用于 cmdlet 的执行对象。

    • $False 包含 FALSE。可以使用此变量在命令和脚本中表示 FALSE,而不是使用字符串”false”。如果该字符串转换为非空字符串或非零整数,则可将该字符串解释为 TRUE。

    • $ForEach 包含 ForEach-Object 循环的枚举数。可以对 $ForEach 变量的值使用枚举数的属性和方法。此变量仅在运行 For 循环时存在,循环完成即会删除。

    • $Home 包含用户的主目录的完整路径。此变量等效于 %homedrive%%homepath% 环境变量。

    • $Host 包含一个对象,该对象表示 Windows PowerShell 的当前主机应用程序。可以使用此变量在命
      令中表示当前主机,或者显示或更改主机的属性,如 $Host.version$Host.CurrentCulture

    • $Input 一个枚举数,它包含传递给函数的输入。$Input 变量区分大小写,只能用于函数和脚本块。(脚本块本质上是未命名的函数。)在函数的 Process 块中,$Input 变量包含当前位于管道中的对象。在 Process 块完成后,$Input 的值为 NULL。如果函数没有 Process 块,则 $Input 的值可用于 End 块,它包含函数的所有输入。

    • $LastExitCode 包含运行的最后一个基于 Windows 的程序的退出代码。

    • $Matches-match-not match 运算符一起使用。将标量输入提交给 -match-notmatch 运算符时,如果检测到匹配,则会返回一个布尔值,并使用由所有匹配字符串值组成的哈希表填充 $Matches 自动变量。

    • $MyInvocation 包含一个对象,该对象具有有关当前命令(如脚本、函数或脚本块)的信息。可以使用该对象中的信息(如脚本的路径和文件名 ($myinvocation.mycommand.path) 或函数的名称
      ($myinvocation.mycommand.name))来标识当前命令。对于查找正在运行的脚本的名称,这非常有用。

    • $PID 包含承载当前 Windows PowerShell 会话的进程的进程标识符 (PID)。

    • $Profile 包含当前用户和当前主机应用程序的 Windows PowerShell 配置文件的完整路径。可以在命令中使用此变量表示配置文件。例如,可以在命令中使用此变量确定是否已创建某个配置文件:

      test-path $profile
      

      也可以在命令中使用此变量创建配置文件:

      new-item -type file -path $pshome -force
      

      此外,还可以在命令中使用此变量在记事本中打开配置文件:

      notepad $profile
      
    • $PSBoundParameters 包含活动参数及其当前值的字典。只有在声明参数的作用域(如脚本或函数)中,此变量才有值。可以使用此变量显示或更改参数的当前值,也可以将参数值传递给其他脚本或函数。

    • $PsCmdlet 包含一个对象,该对象表示正在运行的 cmdlet 或高级函数。

    • $PsCulture 包含操作系统中当前所用的区域性的名称。区域性确定数字、货币和日期等项的显示格式。这是系统的 System.Globalization.CultureInfo.CurrentCulture.Name 属性的值。要获取系统
      的 System.Globalization.CultureInfo 对象,请使用 Get-Culture cmdlet。

    • $PSDebugContext 在调试期间,此变量包含有关调试环境的信息。在其他时间,此变量包含 NULL 值。

    • $PsHome 包含 Windows PowerShell 的安装目录的完整路径

    • $PSScriptRoot 包含要从中执行脚本模块的目录。通过此变量,脚本可以使用模块路径来访问其他资源。

    • $Pwd 包含一个路径对象,该对象表示当前目录的完整路径。

    • $ShellID 包含当前 shell 的标识符。

    • $SourceArgs 包含表示正在被处理的事件的事件参数的对象。此变量只在事件注册命令的 Action
      块内填充。此变量的值也可在 Get-Event 返回的 PSEventArgs (System.Management.Automation.PSEventArgs) 对象的 SourceArgs 属性中找到。

    • $SourceEventArgs 包含一个对象,该对象表示从正在被处理的事件的 EventArgs 中派生出的第一个事件参数。此变量只在事件注册命令的 Action 块内填充。

    • $This 在定义脚本属性或脚本方法的脚本块中,This 变量引用要扩展的对象。

    • $True 包含 TRUE。可以在命令和脚本中使用此变量表示 TRUE。

    环境变量

    PowerShell 可以直接操作环境变量。$env: 中的环境变量只是机器环境变量的一个副本,即使你更改了它,下一次重新打开时,又会恢复如初。(.NET方法更新环境变量除外)。

    我们可以将受信任的文件夹列表追加到环境变量的末尾,这样就可以直接通过相对路径执行这些文件下的文件或者脚本,甚至省略扩展名都可以。

    • 读取特殊的环境变量
      通过环境变量读取Windows操作系统的安装路径,和默认应用程序的安装路径。
    PS> $env:windir
    C:\Windows
    PS> $env:ProgramFiles
    C:\Program Files
    

    插入到文本:

    PS> "My computer name $env:COMPUTERNAME"
    My computer name MYHome-test-01
    
    • 查找环境变量
      Powershell把所有环境变量的记录保存在 env: 虚拟驱动中,因此可以列出所有环境变量。一旦查出环境变量的名字就可以使用 $env:name 访问了。
    PS> ls env:
    Name                           Value
    ----                           -----
    ALLUSERSPROFILE                C:\ProgramData
    APPDATA                        C:\User\sv-test\Home\AppData\Roaming
    CommonProgramFiles             C:\Program Files\Common Files
    
    • 创建新的环境变量
      创建新环境变量的方法和创建其它变量一样,只需要指定env:虚拟驱动器即可
    $env:TestVar1="This is my environment variable"
    $env:TestVar2="Hollow, environment variable"
    
    • 删除和更新环境变量
    # 删除
    del env:TestVar2
    # 更新
    $env:OS="Redhat Linux"
    
    • 环境变量更新生效
      .NET方法 [environment]::SetEnvironmentvariable 操作可以立刻生效。
      下面的例子对当前用户设置环境变量,经测试,重新打开powershell仍然存在。
    PS> [environment]::SetEnvironmentvariable("Path", ";c:\powershellscript", "User")
    PS> [environment]::GetEnvironmentvariable("Path", "User")
    ;c:\powershellscript
    

    4. 管道、重定向

    管道

    把上一条命令的输出作为下一条命令的输入。

    例如通过ls获取当前目录的所有文件信息,然后通过 Sort -Descending 对文件信息按照Name降序排列,最后将排序好的文件的Name和Mode格式化成Table输出。

    PS C:\PStest> ls | sort -Descending Name | Format-Table Name,Mode
    
    Name                                                        Mode
    ----                                                        ----
    d.txt                                                       -a---
    c.txt                                                       -a---
    b.txt                                                       -a---
    ABC                                                         d----
    a.txt                                                       -a---
    

    输出结果

    参考:格式化管道结果

    • 查看所有以Format打头的命令
    Get-Command -Verb format
    

    吸收输出结果

    有的命令无论执行成功或失败都会有输出,有时不需要这些输出时可以使用 | Out-Null,这条命令的作用和 >$null 一样。

    md ABD >$null
    md ABE | Out-Null
    

    导出 csv

    Get-Service | Export-Csv a.csv
    

    导出 html

    Get-Service | ConvertTo-Html -Title "ls result" | Out-File a.html
    

    导出 json

    Get-Service | ConvertTo-Json | Out-File a.json
    

    重定向

    把命令的输出保存到文件中,> 为覆盖,>> 追加。

    PS C:\PStest> "Powershell Routing" >test.txt
    PS C:\PStest> Get-Content .\test.txt
    Powershell Routing
    PS C:\PStest> "Powershell Routing" >>test.txt
    PS C:\PStest> "Powershell Routing" >>test.txt
    PS C:\PStest> "Powershell Routing" >>test.txt
    PS C:\PStest> "Powershell Routing" >>test.txt
    PS C:\PStest> "Powershell Routing" >>test.txt
    PS C:PStest\> Get-Content .\test.txt
    Powershell Routing
    Powershell Routing
    Powershell Routing
    Powershell Routing
    Powershell Routing
    Powershell Routing
    PS C:\PStest>
    

    5. 外部命令

    Powershell 能够像CMD一样很好的执行外部命令。

    • 通过netstat查看网络端口状态
    PS C:\PS> netstat
    
    • 通过IPConfig查看自己的网络配置
    PS C:\PS> ipconfig
    
    • route print查看路由信息
    PS C:\PS> route print
    
    • 启动CMD控制台
      启动CMD控制台键入cmd或者cmd.exe,退出cmd可以通过命令 exit
    PS C:\PS> cmd
    Microsoft Windows [Version 6.1.7601]
    Copyright (c) 2009 Microsoft Corporation.  All rights reserved.
    
    C:\PS>exit
    PS C:\PS>
    
    • 查找可用的Cmd控制台命令
      Cmd.exe 通过 /c 来接收命令参数,在Cmd中help可以查看可用的命令,所以可以通过 Cmd /c help 查找可用的Cmd控制台命令
    PS C:\PS> cmd /c help
    
    • 启动外部程序
      为什么可以通过 notpad 打开记事本,不能通过 wordpad 打开写字板?
      因为notepad.exe位于C:Windows\system32 这个目录,而这个目录已经默认被包含在Powershell的环境变量 $env:Path 中。

      默认键入一个字符串,powershell会将它原样输出,如果该字符串是一个命令或者启动程序,在字符串前加 & 可以执行命令,或者启动程序。

    PS C:\PS> "ls"
    ls
    PS C:\PS> &"ls"
    
        Directory: C:\PS
    
    Mode                LastWriteTime     Length Name
    ----                -------------     ------ ----
    d----        2011/11/23     17:25            ABC
    -a---        2011/11/23     17:36         14 a.txt
    -a---        2011/11/23     17:25          0 b.txt
    
    PS C:\PS> "cmd.exe"
    cmd.exe
    PS C:\PS> &"cmd.exe"
    Microsoft Windows [Version 6.1.7601]
    Copyright (c) 2009 Microsoft Corporation.  All rights reserved.
    
    • 外部调用 PowerShell 脚本
    powershell "& 'C:\xxx\xxx.ps1'"
    

    6. 别名

    • 查询别名所指的真实cmdlet命令
    Get-Alias -name ls
    
    • 查看可用的别名
      可以通过 ls alias: 或者 Get-Alias
    # 列出别名
    ls alias:
    # 列出以 Remove 打头的cmdlet命名的别名
    dir alias: | where {$_.Definition.Startswith("Remove")}
    # 说明:dir alias:获取的是别名的数组,通过where对数组元素进行遍历,$_代表当前元素,alias的Definition为String类型,因为powershell支持.net,.net中的string类有一个方法Startswith。通过where过滤集合在powershell中使用非常广泛。
    
    • 创建自己的别名
    # 给记事本设置一个别名 Edit
    Set-Alias -Name Edit -Value notepad
    # 执行 Edit 打开记事本
    Edit
    
    • 删除自己的别名
    del alias:Edit
    
    • 保存自己的别名
    # 导出别名保存
    Export-Alias alias.ps1
    # 强制导入别名
    Import-Alias -Force alias.ps1
    

    7. 条件判断

    运算符

    运算符 描述
    -eq 等于
    -ne 不等于
    -gt 大于
    -ge 大于等于
    -lt 小于
    -le 小于等于
    -contains 包含
    -notcontains 不包含
    -not 求反, 布尔 逆
    -and
    -or
    -xor 异或
    -like 字符串包含, 支持通配符 ( ?, *)

    可以将比较表达式直接输入进Powershell控制台,然后回车,会自动比较并把比较结果返回。

    PS C:Powershell> (3,4,5 ) -contains 2
    False
    PS C:Powershell> (3,4,5 ) -contains 5
    True
    PS C:Powershell> (3,4,5 ) -notcontains 6
    True
    PS C:Powershell> 2 -eq 10
    False
    PS C:Powershell> "A" -eq "a"
    True
    PS C:Powershell> "A" -ieq "a"
    True
    PS C:Powershell> "A" -ceq "a"
    False
    PS C:Powershell> 1gb -lt 1gb+1
    True
    PS C:Powershell> 1gb -lt 1gb-1
    False
    

    比较数组和集合

    # 过滤数组中的元素
    1,2,3,4,3,2,1 -eq 3
    1,2,3,4,3,2,1 -ne 3
    
    # 验证一个数组是否存在特定元素
    1,9,4,5 -contains 9
    1,9,4,5 -contains 10
    1,9,4,5 -notcontains 10
    

    Where-Object 条件过滤

    • 知道每个对象支持那些属性
    # 查看 Get-Process 返回类型有那些属性
    Get-Process | select -First 1 | fl *
    
    # 使用Get-Process返回所有的当前进程, 然后进程名过滤所有记事本进程
    Get-Process | Where-Object {$_.Name -eq "notepad"}
    
    # 根据进程名过滤所有IE进程
    Get-Process | Where-Object {$_.Name -eq "iexplore"}
    
    # 根据company过滤所有产品发布者以”Microsoft”打头的进程
    Get-Process | Where-Object {$_.company -like '*Microsoft*' } | select Name,Description,Company
    
    # 使用别名
    # 因为Where-Object的使用概率比较高,所以有一个很形象的别名 ? 可以使用:
    Get-Service | ? {$_.Name -like "B*"}
    
    

    IF-ELSEIF-ELSE 条件

    语句模板:

    If(条件满足){
        如果条件满足就执行代码
    }
    Else
    {
        如果条件不满足
    }
    

    示例:

    if($n -gt 15) {"$n  大于 15 " }
    if($n -gt 5) {"$n  大于 5 " }
    if($n -lt 0 ){"-1" } elseif($n -eq 0){"0"} else {"1"}
    

    Switch 条件

    基本语法

    # 使用 Switch
    switch($value) {
        1 {"Beijing"}
        2 {"Shanghai"}
        3 {"Tianjin"}
        4 {"Chongqing"}
        {$_  -gt 10} {"大于10"}
        Default {"没有匹配条件"}
    }
    

    测试取值范围

    使用 Switch 时缺省的比较运算符为 -eq。 等于,你也可以自己定制比较条件,将条件放在花括号中,必须保证条件表达式的返回值为布尔类型 $True$False

    $value=18
    # 使用 Switch 测试取值范围
    switch($value)
    {
        {$_ -lt 10} {"小于10"}
        10  {"等于10"}
        {$_  -gt 10} {"大于10"}
    }
    #输出
    #大于10
    

    多个条件匹配

    $value=2
    # 使用 Switch 测试取值范围
    switch($value)
    {
        {$_ -lt 5 }  { "小于5" }
        {$_ -gt 0 }   { "大于0" }
        {$_ -lt 100}{ "小于100"}
        Default {"没有匹配条件"}
    }
     
    #小于5
    #大于0
    #小于100
    

    Break 关键字

    如果碰到匹配条件时只处理一次,可以使用 Break 关键字

    $value=99
    # 使用 Switch 测试取值范围
    switch($value)
    {
        {$_ -lt 5 }   { "小于5"; break}
        {$_ -gt 0 }   { "大于0"; break}
        {$_ -lt 100}  { "小于100"; break}
        Default {"没有匹配条件"}
    }
     
    # 大于0
    

    比较字符串

    $domain="www.mossfly.com"
    switch($domain)
    {
        "Www.moSSfly.com" {"Ok 1"}
        "www.MOSSFLY.com" {"Ok 2" }
        "WWW.mossfly.COM" {"Ok 3"}
    }
    Ok 1
    Ok 2
    Ok 3
    

    大小写敏感

    Switch有一个 -case 选项,一旦指定了这个选项,比较运算符就会从 -eq 切换到 -ceq,即大小写敏感比较字符串。

    $domain="www.mossfly.com"
    #大小写敏感
    switch -case ($domain)
    {
        "Www.moSSfly.com" {"Ok 1"}
        "www.MOSSFLY.com" {"Ok 2" }
        "www.mossfly.com" {"Ok 3"}
    }
    #Ok 3
    

    使用通配符

    在Switch语句后要指定 -wildcard 选项

    $domain="www.mossfly.com"
    #使用通配符
    switch -wildcard($domain)
    {
        "*"     {"匹配'*'"}
        "*.com" {"匹配*.com" }
        "*.*.*" {"匹配*.*.*"}
    }
    匹配'*'
    匹配*.com
    匹配*.*.*
    

    正则表达式

    给Switch关键字指定选项-regex 选项

    $mail="www@mossfly.com"
    #使用通配符
    switch -regex ($mail)
    {
        "^www"     {"www打头"}
        "com$"     {"com结尾" }
        "d{1,3}.d{1,3}.d{1,3}.d{1,3}" {"IP地址"}
    }
     
    #www打头
    #com结尾
    

    同时处理多个值

    Switch支持对集合所有元素进行匹配,下面的例子使用Powershell Switch语句演示打印水仙花数:

    $value=100..999
    switch($value)
    {
    {[Math]::Pow($_%10,3)+[Math]::Pow( [Math]::Truncate($_%100/10) ,3)+[Math]::Pow( [Math]::Truncate($_/100) , 3) -eq $_} {$_}
    }
     
    #153
    #370
    #371
    #407
    

    8. 循环

    ForEach-Object 循环

    Powershell管道就像流水线,对于数据的处理是一个环节接着一个环节,如果你想在某一环节对流进来的数据逐个细致化的处理,可是使用 ForEach-Object,$_ 代表当前的数据 (当然也允许通过 $_. 调用该对象支持的方法)。

    • 结合条件处理
    Get-WmiObject Win32_Service | ForEach-Object {
        if ($_.ProcessId -gt 3000)
        { "{0}({1})" -f $_.DisplayName,$_.ProcessID}
    }
    
    • 调用方法
    # 杀死所有IE进程
    Get-Process iexplore | ForEach-Object {$_.kill()}
    

    ForEach 循环

    foreach为遍历集合、数组或对象的关键字。

    # 示例1
    $array=7..10
    foreach ($n in $array)
    {
        $n*$n
    }
    #49
    #64
    #81
    #100
    
    # 示例2
    # 这里只为了演示foreach,其实可以用Foreach-Object更简洁。
    foreach($file in dir c:\windows)
    {
        if($file.Length -gt 1mb)
        {
            $File.Name
        }
    }
     
    #explorer.exe
    #WindowsUpdate.log
    
    

    For 循环

    # 示例1
    $sum=0
    for($i=1;$i -le 100;$i++)
    {
        $sum+=$i
    }
    $sum
    
    # 示例2
    $sum=0
    $i=1
    for(;$i -le 100;)
    {
        $sum+=$i
        $i++
    }
    $sum
    

    For循环的特殊应用: 判断域名

    for($domain="";!($domain -like "www.*.*");$domain=Read-Host "Input domain")
    {
        Write-Host -ForegroundColor "Green" "Please give a valid domain name."
    }
    Please give a valid domain name.
    Input domain: www
    Please give a valid domain name.
    Input domain: mossfly.com
    Please give a valid domain name.
    

    逐行读取文本文件

    for($file=[IO.File]::OpenText("c:autoexec.bat") ; !($file.EndOfStream);$line=$file.ReadLine() )
    {
        $line;
    }
    $file.Close()
    REM Dummy file for NTVDM
    

    Do While 循环

    Do和While可能产生死循环,为了防止死循环的发生,你必须确切的指定循环终止的条件。指定了循环终止的条件后,一旦条件不满足就会退出循环。

    • Do While
    # 输入0才中止循环
    do { $n=Read-Host } while( $n -ne 0)
    
    • While
    $n=5
    while($n -gt 0)
    {
        $n
        $n=$n-1
    }
    

    Switch 循环

    在Powershell中由于Switch支持集合,所以也可以使用它进行循环处理。有时对集合的处理,在循环中还须条件判断,使用Switch循环可以一部到位。

    #使用Switch循环
    $nums = 10..7
    Switch ($nums)
    {
    Default { "n= $_" }
    }
    
    $nums = 10..7
    Switch ($nums)
    {
        {($_ % 2) -eq 0} {"$_ 偶数"}
        {($_ % 2) -ne 0} {"$_ 奇数"}
    }
    

    终止当前循环

    使用continue关键字,可是终止当前循环,跳过continue后其它语句,重新下一次循环。

    $n=1
    while($n -lt 6)
    {
        if($n -eq 4)
        {
            $n=$n+1
            continue
     
        }
        else
        {
            $n
        }
        $n=$n+1
    }
    

    跳出循环语句

    跳出循环语句使用break关键字。

    $n=1
    while($n -lt 6)
    {
        if($n -eq 4)
        {
            break
        }
        $n
        $n++
    }
    

    9. 函数和脚本

    函数语法

    Function FuncName (args[])
    {
          code;
    }
    
    • 使用函数作为别名
    # 假如Powershell不支持”cd..” 命令,你可以通过定义函数实现这个功能:
    Function cd.. { cd ..}
    cd..
    
    # 假如Powershell不支持Ping命令,也可以如法炮制:
    Function Ping2 { PING.EXE  -n 1 $args }
    Ping2 www.mossfly.com
    

    更新函数

    # 将当前的函数保存到ps1文件中
    $function:MyPing | Out-File myPing.ps1
    # 重新定义
    $function:MyPing { PING.EXE  -n 1 $args }
    

    删除函数

    del Function:myPing
    

    参数

    函数的参数有3个特性:

    • 任意参数:内部变量 $args 接受函数调用时接受的参数,`$args 是一个数组类型。
    • 命名参数:函数的每一个参数可以分配一个名称,在调用时通过名称指定对应的参数。
    • 预定义参数:函数在定义参数时可以指定默认值,如果调用时没有专门指定参数的值,就会保持默认值。
    # 万能参数 $args
    function sayHello
    {
        if($args.Count -eq 0)
        {
            "No argument!"
        }
        else
        {
            $args | foreach {"Hello,$($_)"}
        }
    }
    sayHello
    sayHello LiLi
    sayHello LiLi Lucy Tom
    
    # 设置参数名称
    function StringContact($str1,$str2)
    {
        return $str1+$str2
    }
    StringContact moss fly
    StringContact -str1 word -str2 press
    
    # 给参数定义默认值
    function stringContact($str1="moss",$str2="fly")
    {
        return $str1+$str2
    }
    stringContact Good Luck
    
    # 使用强类型参数
    function subtract([int]$value1,[int]$value2)
    {
        return $value1-$value2
    }
    
    function subtract([double]$value1,[double]$value2)
    {
        return $value1-$value2
    }
    subtract 8.1  7.9
    #输出 0.199999999999999
    
    # 限制日期类型
    # 函数的参数解释器会自动尝试将字符串转换成日期类型,如果转换失败就是抛出异常
    function DayOfWeek([datetime]$date)
    {
        return  $date.DayOfWeek
    }
    

    Switch 参数

    Powershell函数最简单的参数类型为布尔类型,除了使用Bool类型,也可以使用Switch关键字。
    下面的函数逆转字符串,但是可以通过$try 参数进行控制,如果没有指定$try的值,默认值为$false

    function  tryReverse( [switch]$try , [string]$source )
    {
        [string]$target=""
        if($try)
        {
            for( [int]$i = $source.length -1; $i -ge 0 ;$i--)
            {
                $target += $source[$i]
            }
            return $target
        }
        return $source
    }
    tryReverse -source www.mossfly.com
    tryReverse -try $true -source www.mossfly.com
     
    # www.mossfly.com
    # moc.ylfssom.www
    
    

    返回值

    如果一个函数返回一个值,像其它编程语言一样,这个值包括她的类型信息会直接返回。但是如果遇到多个返回值,Powershell会将所有的返回值自动构造成一个Object数组。可以通过索引访问数组。

    function Square([double]$num)
    {
        return $num*$num
    }
    # 在控制台输出结果
    Square 9.87
    
    # 将结果赋值给变量
    $value=Square 9.87
    
    # 函数有4个返回值 
    function gbMeasure($amount)
    {
        "$amount GB=$($amount) GB"
        "$amount GB=$($amount*1gb/1mb) MB"
        "$amount GB=$($amount*1gb/1kb) KB"
        "$amount GB=$($amount*1gb) B"
    }
    gbMeasure 1
    # 1 GB=1 GB
    # 1 GB=1024 MB
    # 1 GB=1048576 KB
    # 1 GB=1073741824 B
    
    # 将所有的返回值存储在一个变量中
    # 所有的返回值会自动存储在一个数组中
    $result=gbMeasure 1
    
    # 通过索引访问每个返回值
    $result[3]
    
    

    Return语句

    Powershell会将函数中所有的输出作为返回值,但是也可以通过return语句指定具体的我返回值。
    Return 语句会将指定的值返回,同时也会中断函数的执行,return后面的语句会被忽略。

    function test($num)
    {
        1
        9
        return 10
        4
        6
    }
    test
    # 1 和 9 作为输出会返回
    # return语句中的10 也会返回
    # return 语句后的4和6会被忽略
     
    #1
    #9
    #10
    

    消除返回值

    如果要过滤注释,只输出,不作为返回值,可以使用 Write-Host 命令。还可以使用 Write-Debug 命令,只在调试模式下输出。

    Write-debug 的行为受 $DebugPreference 的影响,$DebugPreference 值默认为 SilentlyContinue,此时 Write-debug 不会输出任何信息。

    • $DebugPreference可选的配置如下:

      • SilentlyContinue:调试关闭
      • Stop: 输出调试信息,终止脚本执行
      • Continue: 输出调试信息,继续执行脚本
      • Inquire:输出调试信息,询问用户是否继续执行。
    Function Test()
    {
        Write-Host "Try to calculate."
        "3.1415926"
        Write-Host "Done."
    }
    
    # 在变量值中保存返回值,在控制台输出注释行
    $value=Test
    # Try to calculate.
    # Done.
     
    # 测试返回值
    $value
    # 3.1415926
    
    # 使用调试命令
    Function Test()
    {
        Write-Debug "Try to calculate."
        "3.1415926"
        Write-Debug "Done."
    }
    
    # Debug调试信息只会在调试模式下被输出
    $value=Test
    # 3.1415926
     
    #如果你想通过显示调试信息调试函数,可以开启调试模式
    $DebugPreference="Continue"
    $value=Test
    # 调试: Try to calculate.
    # 调试: Done.
     
    # 测试返回值
    $value
    # 3.1415926
     
    #如果关闭调试模式,这些调试信息自然不会输出
    $DebugPreference="SilentlyContinue"
    $value=Test
    
    

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

    抑制错误信息

    Function ErrorTest()
    {
        # 从这里开始隐藏所有的错误信息
        $ErrorActionPreference="SilentlyContinue"
        Stop-Process -Name "www.mossfly.com"
        # 该进程不存在
        # 恢复$ErrorActionPreference, 错误开始输出
        $ErrorActionPreference="Continue"
    }
    
    # 错误信息不会输出
    ErrorTest
    

    查看支持的函数

    dir function: | ft -AutoSize
    # 查看有多少个函数
    (dir function:).count
    

    查看函数内部定义

    如果你想深入查看函数的内部定义可以直接访问 Function (不带括号)。

    $function:prompt
    

    常用预定义函数

    - `Clear-Host` 清除屏幕的缓存
    - `help`, `man` 查看命令的帮助文档
    - `mkdir`, `md` 通过new-Item创建子目录
    - `more` 分屏输出管道结果
    - `prompt` 返回提示文本
    - `TabExpansion` Tab键的自动完成提示
    - `X`: 调用Set-Location定位到指定的驱动器根目录
    

    自定义Prompt

    在控制台的任何位置输出文本(自定义光标的位置),因为控制台的内容存放在控制台屏幕的缓存中,因此你可以逐个访问内容的每一行或每一个字符。你甚至可以在控制台的屏幕的任何位置输出你想要输出的信息,接下来的函数会演示这个功能。要完成这个功能,需要使用$Host.UI.Rawui , 光标的位置通过屏幕的横坐标(X)和纵坐标(Y)确定,下面的函数会首先记住当前光标的位置,然后在横坐标上增加60个占位符,然后重置光标的位置至当前位置,最后通过prompt函数回复光标的原始位置。

    function prompt
    {
        $curPos = $host.ui.rawui.CursorPosition
        $newPos = $curPos
        $newPos.X+=60
        $host.ui.rawui.CursorPosition = $newPos
        Write-Host ("{0:D} {0:T}" -f (Get-Date)) -foregroundcolor Yellow
        $host.ui.rawui.CursorPosition = $curPos
        Write-Host ("PS " + $(get-location) +">") -nonewline -foregroundcolor Green
    " "
    }
    
    # 运行结果
    PS E:mossfly.com> function prompt
    >> {
    >>     $curPos = $host.ui.rawui.CursorPosition
    >>     $newPos = $curPos
    >>     $newPos.X+=60
    >>     $host.ui.rawui.CursorPosition = $newPos
    >>     Write-Host ("{0:D} {0:T}" -f (Get-Date)) -foregroundcolor Yellow
    >>     $host.ui.rawui.CursorPosition = $curPos
    >>     Write-Host ("PS " + $(get-location) +">") -nonewline -foregroundcolor Gre
    en
    >> " "
    >> }
    >>
    PS E:mossfly.com>                                          2012年2月28日 8:54:01
    

    使用窗口标题栏

    控制台的标题栏有一部分空间,可以放置一些有用的信息,比如当前哪个用户登录在控制台,可以通过设置$host.UI.RawUI.WindowTitle来自定义控制台标题栏的文本。

    管道函数

    Function MarkEXE
    {
        begin
        {
            # 记录控制台的背景色
            $oldcolor = $host.ui.rawui.ForegroundColor
        }
        process
        {
            # 当前管道的元素 $_
            # 如果后缀名为 ".exe",
            # 改变背景色为红色:
            If ($_.name.toLower().endsWith(".exe"))
            {
                $host.ui.Rawui.ForegroundColor = "red"
            }
            Else
            {
                # 否则, 使用正常的背景色:
                $host.ui.Rawui.ForegroundColor = $oldcolor
             }
            # 输出当前的背景色
            $_
          }
        end
        {
            # 最后,恢复控制台的背景色:
            $host.ui.Rawui.ForegroundColor = $oldcolor
         }
    }
    
    Dir c: -recurse | MarkEXE
    

    脚本传参

    • 传递给一个函数或者一个脚本的参数都保存在$args变量中。

      # 脚本 MyScript.ps1 中的代码
      For($i=0;$i -lt $args.Count; $i++)
      {
          Write-Host "parameter $i : $($args[$i])"
      }
      
      # 控制台测试
      .MyScript.ps1 www moss fly com
      
      parameter 0 : www
      parameter 1 : moss
      parameter 2 : fly
      parameter 3 : com
      
    • 在脚本中使用参数名
      输入以下的脚本 (其中param给参数指定名称。):

      param($Directory,$FileName)
      
      "Directory= $Directory"
      "FileName=$FileName"
      

      执行脚本:

      PS E:> .MyScript.ps1 -Directory $env:windir -FileName config.xml
      Directory= C:windows
      FileName=config.xml
      
      PS E:> .MyScript.ps1 -FileName config.xml -Directory $env:windir
      Directory= C:windows
      FileName=config.xml
      
    • 验证参数

      param(
      [string]$Name=$(throw "Parameter missing: -name Name") ,
      [int]$Age=$(throw "Parameter missing: -age x as number")
      )
       
      "Name= $Name"
      "Age=$Age"
      

    管道脚本

    如果你在脚本中使用管道,脚本收集上一个语句的执行结果,默认保存在$input自动变量中。但是直到上一条语句完全执行彻底,管道脚本才会执行。

    • 低速顺序模式

      # 管道脚本 pipeline.ps1
      foreach ($element in $input)
      {
          if($element.Extension -eq ".exe")
          {
              Write-Host -fore "red" $element.Name
          }
          else
          {
              Write-Host -fore "Green" $element.Name
          }
      }
      
      # 执行脚本
      ls $env:windir | .pipeline.ps1
      
      # 如果这样执行, 控制台会被冻结,因为存储的中间结果在玩命的吃内存
      ls $env:windir  -Recurse | .pipeline.ps1
      
      
    • 高速流模式

      # 管道脚本 pipeline2.ps1
      begin
      {
          Write-Host "管道脚本环境初始化"
      }
      process
      {
          $ele=$_
          if($_.Extension -ne "")
          {
              switch($_.Extension.tolower())
              {
                  ".ps1" {"脚本文件:"+ $ele.name}
                  ".txt" {"文本文件:"+ $ele.Name}
                  ".gz"  {"压缩文件:"+ $ele.Name}
              }
          }
      }
      end
      {
          Write-Host "管道脚本环境恢复"
      }
      
      # 执行脚本
      ls | .pipeline2.ps1
      
      # 输出
      管道脚本环境初始化
      文本文件:a.txt
      压缩文件:Metrol.tar.gz
      脚本文件:MyScript.ps1
      脚本文件:pipeline.ps1
      脚本文件:PSLib.ps1
      管道脚本环境恢复
      
    • 自动执行脚本之profile
      profile用来保存一些基本的初始化工作。有四种不同的profile:

      • 所有用户 $pshome\profile.ps1
      • 所有用户(私有) $pshome\Microsoft.PowerShell_profile.ps1
      • 当前用户 $((Split-Path $profile -Parent)+ “\profile.ps1”)
      • 当前用户(私有) $profile
      notepad $((Split-Path $profile -Parent) + “profile.ps1”) 
      # 打开的记事本中输入: 
      Set-Alias edit notepad.exe
      # 也就是给notepad添加edit别名,保存关闭
      
      # 带当前用户配置启动, 控制台会调用记事本打开之前的profile
      edit $((Split-Path $profile -Parent) + “profile.ps1”)
      

    10. 错误处理

    试运行:模拟操作

    如果你想知道一个确定的命令会产生什么影响,你可以进行试运行。这时,Powershell不会执行任何对系统有影响的操作,只会告诉你如果没有模拟运行,可能产生什么影响和后果。通过 -whatif 参数。事实上,许多cmdltes都支持试运行。

    让自己的脚本和函数也支持模拟运行,只需要进行简单的整合。多增加一个switch参数。

    function MapDrive([string]$driveletter, [string]$target, [switch]$whatif)
    {
        If ($whatif)
        {
            Write-Host "WhatIf: creation of a network drive " + "with the letter ${driveletter}: at destination $target"
        }
        Else
        {
            New-PSDrive $driveletter FileSystem $target
        }
    }
    # 首先进行模拟运行:
    MapDrive k 127.0.0.1c$ -whatif
    WhatIf: creation of a network drive
    with letter k: at destination 127.0.0.1c$
     
    # 执行命令
    MapDrive k 127.0.0.1c$
    Name Provider Root
    ---- -------- ----
    k FileSystem 127.0.0.1c$
    

    定义容错度

    识别和处理异常

    $?中的错误状态
    $?变量会保存上一步操作是否成功或者失败,也就是告诉你是否发生了错误。可以写一段脚本进行测试。
    
    Remove-Item "文件不存在" -ErrorAction "SilentlyContinue"
    If (!$?)
    {
    "删除文件操作失败";
    break
    };
    "删除文件成功!"
    
    #删除文件操作失败
    

    使用Traps

    使用Traps可以捕获异常,在捕获到异常时,可以在做相应的处理。例如我们在Powershell自动化工作中,出现了异常可以发一封邮件给管理员。

    Trap { "自动化工作发生异常,正在发邮件联系管理员给管理员!"}
    1/$null
    
    # 输出
    自动化工作发生异常,正在发邮件联系管理员给管理员!
    尝试除以零。
    所在位置 D:\Documents\Desktop\IIS\power-shell-demo\demo.3.ps1:2 字符: 1
    + 1/$null
    + ~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], RuntimeException   
        + FullyQualifiedErrorId : RuntimeException
    
    Trap [System.DivideByZeroException] {
    "除数为空!";
    Continue
    }
    Trap [System.Management.Automation.ParameterBindingException] {
    "参数不正确!";
    Continue
    }
     
    Trap [System.Net.WebException]{
    "网络异常!"
    Continue
    }
     
    1/$null
    Dir -MacGuffin
    $wc = new-object System.Net.WebClient
    $wc.DownloadFile("http://www.mossfly.com/powershell.txt","e:ps.txt")
     
    #除数为空!
    #参数不正确!
    #网络异常!
    

    Traps异常发生时使用break中断脚本执行

    Trap { "异常发生,我想终止脚本!" }
    stop-service -name "NoSuchService" -ErrorAction "Stop"
    

    Traps异常发生时,使用Continue 继续执行

    Trap { "异常发生,我要继续执行!" }
    stop-service -name "NoSuchService" -ErrorAction "Continue"
    Write-Host "继续执行"
    

    查看异常的详细信息

    Trap 到异常后,Powershell会自动将该异常保存在 $_ 变量中。

    Trap
    {
    Write-Host $_.Exception.Message;
    Continue
    };
    Stop-Process -Name "NoSuchProcess"
    

    错误记录:详细错误

    # 将错误信息写入 Error.txt 中
    dir "NoSuchDirectory" 2> Error.txt
    # 读取错误信息
    Get-Content Error.txt
    

    抛出自定义异常信息

    Function Func-Test($a,$b)
    {
    if($b -eq $null)
    {
    throw "参数b 不能为空!"
    }
    "{0}+{1}={2}" -f $a,$b,($a+$b)
    }
    Func-Test -a 10
    
    # 优化后
    Function Func-Test($a,$b=$(throw "参数B 不能为空!"))
    {
    "{0}+{1}={2}" -f $a,$b,($a+$b)
    }
    Func-Test -a 10 -b 9
    Func-Test -a 10
    
    

    函数中捕获异常

    Function Test-Func
    {
    Trap { "Trap 到异常了." }
    1/$null
    Get-Process "NoSuchProcess"
    Dir MossFly:
    }
    Test-Func
    
    # 让脚本在第一次遇到异常就停止工作
    Function Test-Func
    {
    Trap {
    "Trap到了异常: $($_.Exception.Message)";
    Break
    }
    1/$null
    Get-Process "NoSuchProcess" -ErrorAction Stop
    Dir MossFly: -ErrorAction Stop
    }
    Test-Func
    
    # 如果将Trap放在函数的调用者中,并跟上Continue,这样函数中第一条错误发生,脚本就会终止执行,并且屏蔽Powershell默认的错误输出。
    function Caller
    {
    Trap
    {
    "Trap Error: $($_.Exception.Message)";
    Continue
    }
    Test-Func
    }
    function Test-Func
    {
    1/$null
    Get-Process "nosuchthing" -ea Stop
    Dir xyz: -ea Stop
    }
    Caller
    #Trap Error: 试图除以零。
    
    # 脚本块
    function test-func
    {
    # 将 Trap 定义在函数外面:
    Trap {"Trap Error: $($_.Exception.Message)"; Continue}
    # 内部函数
    &{
    1/$null
    Get-Process "nosuchthing" -ea Stop
    Dir xyz: -ea Stop
    }
    
     
    }
    test-func
    #Trap Error: 试图除以零。
    

    断点执行

    $DebugPreference="inquire"
    Write-Debug "输入一行调试信息"; 
    Write-Host "伦敦奥运会女子体操决赛"
    

    还可以通过 Set-PSDebug -step 进行单步跟踪,Powershell会每只行一段代码,就会向用户询问是否继续执行。

    设置进度条的显示模式

    《官方文档》

    Write-Progress
         [-Activity] <String>
         [[-Status] <String>]
         [[-Id] <Int32>]
         [-PercentComplete <Int32>]
         [-SecondsRemaining <Int32>]
         [-CurrentOperation <String>]
         [-ParentId <Int32>]
         [-Completed]
         [-SourceId <Int32>]
         [<CommonParameters>]
    

    显示 For 循环的进度

    for ($i = 1; $i -le 100; $i++ )
    {
        Write-Progress -Activity "Search in Progress" -Status "$i% Complete:" -PercentComplete $i
        Start-Sleep -Milliseconds 250
    }
    

    显示嵌套 For 循环的进度

    此示例显示两个嵌套 For 循环的进度,每个循环由一个进度条表示。
    Write-Progress 第二个进度条的命令包含将其与第一个进度条区分开来的Id参数。
    如果没有Id参数,进度条将相互叠加,而不是一个在另一个下方显示。

    for($I = 1; $I -lt 101; $I++ )
    {
        Write-Progress -Activity Updating -Status 'Progress->' -PercentComplete $I -CurrentOperation OuterLoop
        for($j = 1; $j -lt 101; $j++ )
        {
            Write-Progress -Id 1 -Activity Updating -Status 'Progress' -PercentComplete $j -CurrentOperation InnerLoop
        }
    }
    

    显示嵌套流程的每个级别的进度

    您可以使用ParentId参数来缩进输出以显示每个步骤的进度中的父/子关系。

    foreach ( $i in 1..10 ) {
        Write-Progress -Id 0 "Step $i"
        foreach ( $j in 1..10 ) {
            Write-Progress -Id 1 -ParentId 0 "Step $i - Substep $j"
            foreach ( $k in 1..10 ) {
                Write-Progress -Id 2  -ParentId 1 "Step $i - Substep $j - iteration $k"; start-sleep -m 150
            }
        }
    }
    
    Step 1
         Processing
        Step 1 - Substep 2
             Processing
            Step 1 - Substep 2 - Iteration 3
                 Processing
    

    11. 命令发现和脚本块

    发现命令

    使用 Get-Command 命令可以查看当前作用域支持的所有命令。如果你想查看关于 LS 命令的信息,请把它传递给Get-Command。

    PS C:> Get-command LS
    
    CommandType Name Definition
    ----------- ---- ----------
    Alias       ls   Get-ChildItem
    

    查看更详细的信息

    Get-Command ls | fl *
    

    事实上,Get-Command 返回的是一个对象CommandInfo,ApplicationInfo,FunctionInfo,或者CmdletInfo;

    如果一条命令可能指向两个实体,get-command也会返回,例如more。

    PS C:> Get-Command more
    
    CommandType Name     Definition
    ----------- ----     ----------
    Function    more     param([string[]]$paths)...
    Application more.com C:windowsSYSTEM32more.com
    

    一个命令指向多个实体时,Powershell 中有一个机制,就是函数永远处在最高的优先级。

    function ipconfig(){
        return "我是自定义函数的 ipconfig"
    }
    ipconfig
    del Function:ipconfig
    ipconfig
    

    调用操作符 "&"

    如果你之前将Powershell命令存储在了一个字符串中,或者一个变量中。此时,调用操作符 & 就可以将字符串直接解释成命令并执行,如果在Powershell控制台中,你只须要输入即可。

    调用操作符只能接受单个命令

    调用操作符不能接受全部的Powershell脚本或命令,只能接受单个的一条命令。

    直接执行一个CommandInfo对象

    $command=Get-Command tasklist
    & $command
    

    优先级

    可能存在别名,命令,函数的的名称一样,那Powershell会不会纠结到底执行哪个呢?当然不会,因为它们之间是有一个优先级的。从高到底,依次为:

    • Alias(1)
    • Function(2)
    • Filter(2)
    • Cmdlet(3)
    • Application(4)
    • ExternalScript(5)
    • Script (...)

    怎样突破优先级的限制执行指定的命令呢?方法都是大同小异,通过特定信息过滤到指定的CommandInfo对象,然后直接使用我们本篇的调用操作符。

    $command=Get-Command -Name ping | where {$_.CommandType -eq "Application" }
    & $command
    

    语句块

    脚本块是一种特殊的命令模式。一个脚本块可以包含许多的 Powershell命令和语句。它通常使用大括号定义。最小最短的脚本块,可能就是一对大括号,中间什么也没有。可以使用之前的调用操作符 & 执行脚本块:

    PS E:> & {"当前时间:" + (get-date) }
    当前时间:08/08/2012 22:30:24
    

    将命令行作为整体执行

    PS E:> & {$files=ls;Write-Host "文件数:" $files.Count }
    文件数: 29
    

    执行表达式 Invoke-Expression

    Invoke-Expression,这条命令的逻辑就是将一条字符串传递给调用操作符。

    需要注意,在传递给 invoke-expression 的字符串使用了单引号,单引号可以防止变量被替换。如果上面的命令使用了双引号,会先去解释 $_.name,但是当前作用域中,$_.Namenull,所以结果不是期望的。

    PS E:> Invoke-Expression 'Get-Process | Where-Object { $_.Name -like "e*"}'
    
    Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
    -------  ------    -----      ----- -----   ------     -- -----------
        332      29    12280      24264   154     1.40   3236 egui
        386      32    78624      85508   183            1884 ekrn
        284      22     8980      17048    99            1920 EvtEng
       1000      89    55520      83280   355    23.24   2848 explorer
    

    管道中的foreach-object语句块

    管道中的foreach-object本身后面也会带语句块,针对数组中的每一个元素分别传递给语句块处理。

    Get-Process | ForEach-Object { $_.name }
    

    函数本身是一个已命名的语句块

    #定义一个函数
    Function SayHello([string]$people="everyone")
    {
    	write-host "Hello, $people ”
    }
    
    #通过函数调用
    SayHello "Mosser"
    Hello, Mosser
    
    #通过语句块调用
    $func=function:SayHello
    $scriptblocks
    
    param([string]$people="everyone")
    write-host "Hello, $people ”
    
    & $scriptblocks
    Hello, everyone
    

    传递参数给语句块

    定义和传递参数一次性完成,有点匿名函数的味道。

    & { param($people="everyone") write-host "Hello, $people ” } "Mosser"
    Hello, Mosser
    

    Begin, Process, End 管道语句块

    get-process | select -last 5 | & {
    begin {
    "开始准备环境"
    }
    process
    {
     $_.Name
    }
    end {
    "开始清理环境"
    }
    }
    #输出结果为:
    开始准备环境
    wlcommsvc
    WLIDSVC
    WLIDSVCM
    WmiPrvSE
    XDict
    开始清理环境
    

    验证变量

    & { $value1 = 10; $global:value2 = 20 }
    $value1
    $value2
    # 输出
    # 20
    

    执行上下文

    Powershell 提供了一个非常特别的自动化变量,$ExecutionContext。这个对象主要包含两个属性:InvokeCommand 和 SessionState.

    InvokeCommand 处理变量

    在Powershell控制台中遇到三个比较特殊的字符:

    | 特殊字符 | 定义 | 内部方法 |
    | --- | --- | --- |
    | `"` | 处理字符串中的变量	 | ExpandString() |
    | `&` | 执行命令集	 | InvokeScript() |
    | `{}` | 创建一个新的代码块	 | NewScriptBlock() |
    
    # 常规方式
    $site = '大白博客'
    # 双引号中的变量会被自动解析成变量的值:
    $text = "我的个人网站 $site"
    $text
    # 输出: 我的个人网站 大白博客
    
    # 通过ExpandString()自动处理字符串中的变量
    $site = '大白博客'
    $text = '我的个人网站 $site'
    $executioncontext.InvokeCommand.ExpandString($text)
    
    

    脚本块和命令

    # 使用NewScriptBlock方法创建脚本块
    $blockStr='$write=Get-Process WindowsLiveWriter "$($write.Name) 占用内存: $($write.WorkingSet/1mb) MB"'
    $block = $executioncontext.InvokeCommand.NewScriptBlock($blockStr)
    & $block
    # 输出: WindowsLiveWriter 占用内存: 150.734375 MB
    
    # 使用InvokeScript执行命令块
    $cmd='3*3*3.14'
    $executioncontext.InvokeCommand.InvokeScript($cmd)
    # 使用Invoke-Expression执行语句块
    Invoke-Expression $cmd
    

    SessionState 环境对象

    SessionState是一个用来表现Powershell环境的对象, 同样可以通过自动化变量 $ExecutionContext 访问这些信息。

    $executioncontext.SessionState | Format-List *
    

    PSVariable

    PSVariable, 可以取出和更新Powershell中所有的变量.

    $value = "Test"
    # 读取变量
    $executioncontext.SessionState.PSVariable.GetValue("value")
    Test
    # 修改变量:
    $executioncontext.SessionState.PSVariable.Set("value", 100)
    $value
    100
    

    管理驱动器

    # 查看当前驱动器信息
    $executioncontext.SessionState.Drive.Current
    
    # 查看所有驱动器信息
    $executioncontext.SessionState.Drive.GetAll() | ft -
    
    # 查看特定驱动器
    $executioncontext.SessionState.Drive.GetAllForProvider("FileSystem")
    
    

    路径操作

    SessionState的Path包含几个特殊的方法,基本可以覆盖各种常用的路径操作了。

    • CurrentLocation() 当前路径。 对应的命令 Get-Location
    • PopLocation() 获取存储的路径。 对应的命令 Pop-Location
    • PushCurrentLocation() 存储路径。对应的命令 Push-Location
    • SetLocation() 定位路径。对应的命令 Set-Location
    • GetResolvedPSPathFromPSPath() 相对路径转换成绝对路径。对应的命令 Resolve-Location
  • 相关阅读:
    webapi 导入CSV文件
    webapi 导出CSV文件
    .net 压缩文件夹和解压
    SSH免密码登陆以及穿越跳板机
    hbase shell删除键不听使唤
    百度分享插件wbuid属性无法@指定微博
    iOS safari BUG 总结
    论zeroclipboard的各种爽翻天用法
    Android Studio 使用技巧
    安装第三方包web.py发生的错误
  • 原文地址:https://www.cnblogs.com/yangyxd/p/16003769.html
Copyright © 2020-2023  润新知