美文网首页野生技术宅协会
Powershell快速入门(三) 实战应用

Powershell快速入门(三) 实战应用

作者: 乐百川 | 来源:发表于2017-05-29 00:44 被阅读253次

    好像关于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,利用它我们可以获取网页内容、下载文件甚至是填写表单。这个命令的别名是iwrcurlwget。我们就使用它来下载网上的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的简写就是HKCUHKEY_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的话,智能提示会显示这里有AddChartAddChart2两个方法,不过我看了下文档,前面那个过时了,所以这里使用带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

    相关文章

      网友评论

        本文标题:Powershell快速入门(三) 实战应用

        本文链接:https://www.haomeiwen.com/subject/uimpxxtx.html