ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

PowerShell命令与脚本(9)——函数

2021-06-25 23:34:52  阅读:189  来源: 互联网

标签:脚本 函数 Powershell host 参数 返回值 控制台 PowerShell


PowerShell定义函数

函数是自定义的Powershell代码,有三个原则: 简短:函数名简短,并且显而易见。 聚合:函数可以完成多个操作。 封装和扩展:将一批Powershell语句进行封装,实现全新的功能需求。 函数的结构由三部分组成:函数名,参数,函数体 Function FuncName (args[]) { code; }   使用函数作为别名 假如Powershell不支持copy con命令,你可以通过定义函数实现这个功能: 控制台上多行输入定义函数   把函数精简成一行 可以将一个函数定义在一行上,但是这样阅读和理解起来就不方便,可以在每条命令后加分号进行分割。 使用文本编辑器 函数可以在文本编辑器上编写,写完以后复制进Powershell控制台即可。如果控制台设置为快速编辑模式,从记事本复制后,直接在控制台鼠标右键即可完成黏贴。   更新函数 如果要更新已经定义好的函数,简单的方法是重新定义,这样新的定义会覆盖旧的定义。但是如果函数代码没有保存副本,可以先将函数定义导出到ps1文件,然后就可以编辑了。   删除函数 控制台定义的函数只会在当前会话生效,一旦控制台退出,会自动消失。在不关闭控制台的条件下删除一个已经定义好的函数,可是使用虚拟驱动器的方法:    

PowerShell处理函数的参数

Powershell函数可以接受参数,并对参数进行处理。函数的参数有3个特性:
  1. 任意参数:内部变量$args 接受函数调用时接受的参数,$args是一个数组类型。
  2. 命名参数:函数的每一个参数可以分配一个名称,在调用时通过名称指定对应的参数。
  3. 预定义参数:函数在定义参数时可以指定默认值,如果调用时没有专门指定参数的值,就会保持默认值。
1、$args 万能参数 给一个函数定义参数最简单的是使用$args这个内置的参数。它可以识别任意个参数。尤其适用哪些参数可有可无的函数。 function sayHello {     if($args.Count -eq 0)     {         "No argument!"     }     else     {         $args | foreach {"Hello,$($_)"}     } } 因为$arg是一个数组,可以用它求和。   2、设置参数名称   3、给参数定义默认值   4、使用强类型参数 通过之前的例子发现将用户的参数传递给函数显得比较混乱。罪魁祸首就是Powershell的参数解释器,它可以自动处理和分配参数给函数。 函数的参数解释器比较傲慢,它对你提供的参数的信息完全不关心。它只会粗略地将参数进行分割,并且最大限度的进行自动类型转换。事实上,这种类型转换很多时候并不完美。所以最好提前能够对参数进行强类型限制。 限制数字类型 上面的函数执行后,会抛出异常。 因为subtract参数定义了强类型,参数的类型可能引起函数的处理结果。例如调用上面的函数。 结果为0 但是如果将上面的函数的参数定义为Double型,   限制日期类型 函数的参数解释器会自动尝试将字符串转换成日期类型,如果转换失败就是抛出异常。   5、Switch 参数 Powershell函数最简单的参数类型为布尔类型,除了使用Bool类型,也可以使用Switch关键字。 下面的函数逆转字符串,但是可以通过$try 参数进行控制,如果没有指定$try的值,默认值为$false  

PowerShell指定函数的返回值

1、一个或多个返回值 Powershell不像它编程语言,它的函数可以有多个返回值。如果你直接调用函数,返回值会在控制台输出。当然你也可以将结果存储在一个变量中进一步处理。 下面的例子演示返回一个值:
function Square([double]$num)
{
    return $num*$num
}
#在控制台输出结果
Square 9.87
#97.4169
 
#将结果赋值给变量
$value=Square 9.87
$value
#97.4169
 
#返回值为Double类型
$value.GetType().FullName
#System.Double
下面的例子演示返回多个值
function gbMeasure($amount)
{
    "$amount GB=$($amount) GB"
    "$amount GB=$($amount*1gb/1mb) MB"
    "$amount GB=$($amount*1gb/1kb) KB"
    "$amount GB=$($amount*1gb) B"
}
 
#函数返回4个值
gbMeasure 1
# 1 GB=1 GB
# 1 GB=1024 MB
# 1 GB=1048576 KB
# 1 GB=1073741824 B
 
#将所有的返回值存储在一个变量中
$result=gbMeasure 1
$result

# 1 GB=1 GB
# 1 GB=1024 MB
# 1 GB=1048576 KB
# 1 GB=1073741824 B
 
#所有的返回值会自动存储在一个数组中
$result=gbMeasure 1
$result.GetType().Name
 
# Object[]
 
#通过索引访问每个返回值
$result=gbMeasure 1
$result[3]
# 1 GB=1073741824 B
总结一下,如果一个函数返回一个值,像其它编程语言一样,这个值包括她的类型信息会直接返回。但是如果遇到多个返回值,Powershell会将所有的返回值自动构造成一个Object数组。可以通过索引访问数组。   2、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
  访问返回值 一个函数返回了一个值还是多个值,是可以验证的。下面的例子会产生随机数,如果没有指定个数,默认会返回一个随机数,否则会返回指定个数的随机数。
function lottery([int]$number=1)
{
$rand = New-Object system.random
For ($i=1; $i -le $number; $i++) {
$rand.next(1,50)
}
}
# 参数为空时,返回值不是数组:
$result = lottery
$result -is [array]
# False
# 如果指定多个随机数是,返回值是数组类型:
$result = lottery 10
$result -is [array]
# True
  从函数的返回值中消除输出 函数默认会将函数中的所有输出作为函数的返回值返回,这样很方便。但有时可能会将不必要的输出误以为返回值。写脚本程序时,可能需要自定义一些函数,这个函数可能只需要一个返回值,但是为了提高函数的可读性,可能会在函数增加一些注释输出行。
function Test()
{
    "Try to calculate."
    "3.1415926"
    "Done."
}
 
#保存在变量中输出,
$value=Test
$value
# Try to calculate.
# 3.1415926
# Done.
 
#如果要过滤注释,只输出,不作为返回值,
#可以使用Write-Host命令
Function Test()
{
    Write-Host "Try to calculate."
    "3.1415926"
    Write-Host "Done."
}
# 在变量值中保存返回值,在控制台输出注释行
$value=Test
# Try to calculate.
# Done.
 
# 测试返回值
$value
# 3.1415926
  使用调试信息报告 可能输出这些函数中临时提示信息,给函数的返回值造成干扰。要解决这个问题,除了上述的Write-Host,也可以使用Write-Debug命令。
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()
{
    #该进程不存在
    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查看支持的函数

Powershell已经提供了许多用户能够使用的预定义函数,这些函数可以通过Function:PSDrive虚拟驱动器查看。 从这些结果不但能够看出函数的名称,还能通过Definition列查看函数的内容。如果你想深入查看函数的内部定义可以直接访问Function: Powershell中的这些预定义的函数可以做很多重要的工作。   Clear-Host 清除屏幕的缓存 help,man 查看命令的帮助文档 mkdir,md 通过new-Item创建子目录 more 分屏输出管道结果 prompt 返回提示文本 TabExpansion Tab键的自动完成提示 X: 调用Set-Location定位到指定的驱动器根目录   如果你想查看当前Powershell环境中定义了多少个函数可以通过   自定义Prompt 每次成功执行完一条命令,Powershell就会执行Prompt函数,提示用户进行下一步输入。默认设置中,prompt显示“PS” 和当前的工作目录。 再接着是”>”或”>>”,具体情况要看当前Powershell控制台的的层数。当然你可以自定义prompt的,那就得覆盖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
" "
}
运行结果   使用窗口标题栏 在Windows控制台的标题栏有一部分空间,可以放置一些有用的信息,比如当前哪个用户登录在控制台,可以通过设置$host.UI.RawUI.WindowTitle来自定义控制台标题栏的文本。 下面的例子就会演示设置标题栏文本,通过.NET方法获取当前用户信息,由于该方法会有几秒钟执行时间,为了效率考虑首先将用户信息保存在全局变量中,然后在Prompt函数中调用。
$global:CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
function prompt
{
$host.ui.rawui.WindowTitle = "Line: " + $host.UI.RawUI.CursorPosition.Y + " " + $CurrentUser.Name + " " + $Host.Name + " " + $Host.Version
Write-Host ("PS " + $(get-location) +">")  -nonewline -foregroundcolor Green
return " "
}
执行以后在标题栏会显示: 如果你使用管理员权限运行控制台时,Prompt函数还可以给出警告。使用WindowsPrincipal 辨别当前用户是否使用了管理员权限,你不需要了解下面的.NET代码,它会在全局变量中将布尔值赋值给$Admin。
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = new-object System.Security.principal.windowsprincipal($CurrentUser)
$global:Admin = $principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Administrator)
Function prompt
{
    # 输出标准的提示信息:
    Write-Host ("PS " + $(get-location)) -nonewline
    # The rest depends on whether you have admin rights or not:
    If ($admin)
    {
        $oldtitle = $host.ui.rawui.WindowTitle
        # 将"Administrator: " 显示在标题栏
        If (!$oldtitle.StartsWith("Administrator: "))
        {
            $host.ui.rawui.WindowTitle ="Administrator: " + $oldtitle
        }
        #  Prompt结尾显示红色的尖括号
        Write-Host ">" -nonewline -foregroundcolor Red
     }
     Else
     {
        Write-Host ">" -nonewline
      }
     return " "
}
没有管理员权限时,标题栏文本:Windows Powershell 有管理员权限时,标题栏文本:Administrator :管理员 : Windows Powershell   Clear-Host:删除屏幕缓存 很可能,你已经注意到了,cls可以删除屏幕的缓存。事实上,cls只是Clear-Host函数的别名,但是却看不到这个函数的内容。 在Powershell中短斜杠是个特殊字符,如果一个函数名中包含了特殊字符就应当把它放在花括号中。   盘符名预定义函数C:,D:,E: 这些盘符名称可以作为单独的一个函数,是怎么做到的呢?  

PowerShell函数 过滤器 管道

一个函数能够访问和进一步处理另外一条命令的结果吗?答案是肯定的,这被称为管道。管道有两种模式,一种是顺序处理模式,一种是流处理模式。   低效率的顺序模式:$input 在最简单的情况下,你的函数不是真正支持管道。只能对前一个命令执行后的结果处理。前一个命令执行的结果通过被自动保存在$input变量中,$input是一个数组,它可以包含许多元素,一个元素,甚至一个元素都没有,这取决于具体的环境。 到目前为止,这个函数只是仅仅输出了管道的结果,并没有其它比较强大的功能。在接下来的例子中,函数将会对管道的结果做进一步处理。函数名MarkEXE,将会检查Dir的结果,并高亮标记后缀名为EXE的文件名为红色。
function MarkEXE
{
# 保存控制台当前的前景色
$oldcolor = $host.ui.rawui.ForegroundColor
# 通过循环逐条检查管道的结果
Foreach ($element in $input)
{
    # 如果后缀名为.exe,设置为前景色为红色
    If ($element.name.toLower().endsWith(".exe"))
    {
        $host.ui.Rawui.ForegroundColor = "red"
    }
    Else
    {
        # 否则恢复默认的前景色
        $host.ui.Rawui.ForegroundColor = $oldcolor
    }
    # 输出数组元素
    $element
}
# 最后,重置控制台的前景色:
$host.ui.Rawui.ForegroundColor = $oldcolor
}
  过滤器:高效率 流模式 管道的低效率顺序模式在处理大容量数据时很容易出现问题,其结果是巨大的内存占用和进程等待。如果你的函数支持高效率的流模式,在处理管道结果时仅占用很小的内存。 事实上,针对之前MarkEXE函数,你只需要替换”function” 关键字 为 “filter”,它就会开始流模式处理,这样你再也不用过分的担心忍受程序的无休止的响应和崩溃的危险。你也可以递归处理全盘目录,甚至处理极其庞大的数据。例如: Dir c: -recurse | MarkEXE 当MarkEXE每次被调用时,它只会对当前目录下的每个单独的元素进行处理。对于过滤器filters来说,$input 一直都是一个独立的元素。这也就是为什么在过滤器中$input一点用也没有的道理。此时,最好使用$_ 变量,因为它代表了当前处理的数据。这样还可以简化MarkExe,因为过滤器自身已经扮演了循环的角色了,你没有必要再写专门的循环处理了。
Filter MarkEXE
{
    # 记录当前控制台的背景色
    $oldcolor = $host.ui.rawui.ForegroundColor
    # 当前的管道元素保存在 $_ 变量中
    # 如果后缀名为 ".exe",
    # 改变背景色为红色:
    If ($_.name.toLower().endsWith(".exe"))
    {
        $host.ui.Rawui.ForegroundColor = "red"
    }
    Else
    {
        # 否则使用之前的背景色
        $host.ui.Rawui.ForegroundColor = $oldcolor
    }
    # 输出当前元素
    $_
    # 最后恢复控制台颜色:
    $host.ui.Rawui.ForegroundColor = $oldcolor
}
  开发真正的管道函数 过滤器在函数中属于高级应用,因为它可以立即处理管道结果的每一个元素。但是过滤器必须每次重复执行预定义命令的结果。对于MarkEXE 函数,每次执行的过程中要记录和更新控制台的背景颜色,也要花费资源和时间。 事实上,过滤器只是特殊的函数。如果一个函数内部使用了管道,你就可以定义三个基础的任务区了:第一步,完成函数的初始化,完成函数执行的预备步骤;第二步处理递归调用所得的结果;最后进行收尾工作。 这三个任务区分别可以使用begin,process,end 语句块。 接下来把MarkEXE安装上面的模式进行改装:
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
     }
}
 

标签:脚本,函数,Powershell,host,参数,返回值,控制台,PowerShell
来源: https://www.cnblogs.com/Ulysse/p/14932917.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有