当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 度量元 PowerShell 脚本

作者:精品下载站 日期:2024-12-14 08:06:30 浏览:13 分类:玩电脑

度量元 PowerShell 脚本


前几天我正在摆弄 PowerShell。我每天都在 PowerShell 提示符前度过,并且总是寻找解决问题或回答问题的方法,而无需将手从键盘上移开。出于某种原因,我开始考虑公制转换。 50 米是多少英尺? 21.5 英尺是多少米?对于这种事情,我有一个大概的想法。但不是像将克转换为盎司那样。但我可以查找公式并使用 PowerShell。

$v = 123.456
$v/28.35

此代码段会将克 ($v) 的值转换为盎司。看起来很简单。我什至可以围绕它构建一个函数。

Function Convert-Gram {
    [cmdletbinding()]
    [alias("gr2oz")]
    [outputType([System.Double])]
    Param(
        [Parameter(Position = 0, HelpMessage = "Specify a gram value.")]
        [double]$Value,
        [Parameter(HelpMessage = "Round the result to this many decimal places")]
        [int]$Round = 2
    )
    $from = "grams"
    $to = "ounces"
    Write-Verbose "Converting $value $from to $to"
    [math]::Round($value / 28.35, $round)
{

事实上,我花时间手动创建了从一种单位转换为另一种单位所需的所有函数。

Function Convert-Millimeter {
    [cmdletbinding()]
    [alias("mm2in")]
    [OutputType([System.Double])]
    Param(
        [Parameter(Position = 0, HelpMessage = "Specify a millimeter value.")]
        [double]$Value,
        [Parameter(HelpMessage = "Round the result to this many decimal places")]
        [int]$Round = 2
    )
    $from = "millimeters"
    $to = "inches"
    Write-Verbose "Converting $value $from to $to"
    [math]::Round($value/25.4, $round)
}

当我完成后,我意识到我花了很多时间手动编写脚本。我本可以做的就是让 PowerShell 为我编写函数!

元脚本

我决定看看是否可以编写 PowerShell 代码来编写 PowerShell 代码。首先,我需要一些指导方针。首先,我想要的只是 PowerShell 会话中存在的函数。我不需要文件。我想遵循使用 Convert 动词的标准命名约定。我希望每个命令都有一个简短的别名,以便更容易从控制台使用。

我的元函数需要接受来自单元的参数,例如克,以及单位,例如盎司。这应该生成一个名为 Convert-GramToOunce 的函数,其别名为 gr2oz。新函数名称的构造非常简单。

 $name = "global:Convert-$($from)To$($To)"

您会注意到我使用了 global: 前缀。这是因为,最终我将在 Function: PSDrive 中创建该函数。

New-Item -Path function: -Name $name -Value $value -Force

但由于作用域的原因,当它在元函数中运行时,它不会在我期望的全局作用域中创建该函数。 New-Item cmdlet 没有像某些 cmdlet(即 New-PSDrive)那样的 -Scope 参数。相反,我必须依靠 global: 前缀来给 PowerShell 提示。

该函数的值将是从此处字符串构建的脚本块。

$value = [scriptblock]::Create($body)

这里的字符串最初是我原始函数体的副本。

        $body = @"
$help
[Cmdletbinding()]
[Alias("$alias")]
[OutputType([System.Double])]
Param(
    [Parameter(Position = 0, Mandatory,ValueFromPipeline,HelpMessage = "Specify a $($from.tolower()) value.")]
    [ValidateNotNullOrEmpty()]
    $(If ($Validation) {
        $v = "[$validation]"
        $v
    })
    [double]`$Value,
    [Parameter(HelpMessage = "Round the result to this many decimal places. Specify a value between 0 and 10.")]
    [ValidateRange(0,10)]
    [int]`$Round = 2,
    [Parameter(HelpMessage = "Get a rich object result.")]
    [switch]`$Detailed
    )
    Process {
        Write-Verbose "Converting `$value from $($from.tolower()) to $($to.ToLower()) rounded to `$round decimal places."
        `$r = [math]::Round(($code),`$round)
        if (`$Detailed) {
            [PSCustomObject]@{
                $From = `$Value
                $To = `$r
            }
        }
        else {
            `$r
        }
    }
"@

$code 和 $from 等变量将从参数值扩展。但我必须小心转义像Detailed 这样的变量。我希望最终的代码使用函数中的 $Detailed 。不要用我的元函数中的值替换它。

我应该指出,即使该函数包含 Alias 定义,创建函数项也不会处理该指令。我需要在元函数中手动定义别名。

Set-Alias -Name $alias -Value $name -Scope Global

请注意 Scope 参数的使用。我从名称哈希表构建别名。

$abbreviations = @{
            gram       = "gr"
            ounce      = "oz"
            meter      = "m"
            feet       = "ft"
            millimeter = "mm"
            inch       = "in"
            mile       = "mi"
            kilometer  = "km"
            kilogram   = "kg"
            pound      = "lb"
            fahrenheit = "f"
            celsius    = "c"
            yard       = "yd"
            liter      = "l"
            quart      = "qt"
            milliliter = "ml"
            kelvin     = "k"
}
$alias = "{0}2{1}" -f $abbreviations[$from], $abbreviations[$to]

这就是我创建 gr2oz 别名的方法。

我的元函数采用一个字符串作为转换代码。这是我在原始公制转换函数中使用的算法。

[玩转系统] 度量元 PowerShell 脚本

添加帮助

我还决定自动生成基于评论的帮助。我正在基于模板动态创建函数。生成帮助应该没有什么不同。我可以创建一个此处字符串并放入参数值。我为此任务编写了一个单独的函数。

Function New-MetricFunctionHelp {
    [cmdletbinding()]
    Param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$From,
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$To,
        [string]$Alias,
        [double]$ExampleValue
    )

    $cmd = "Convert-$($From)To$($To)"
    #create the example value object
    $obj = [pscustomobject] @{
        $From = 1
        $To = $ExampleValue
    } | Out-String
    $h = @"
<#
.Synopsis
Convert a value from $From to $To.
.Description
Use this function to convert values between $from and $to. The default output
is the converted value. Or you can use -Detailed to get a rich object. See examples.
.Parameter Value
Specify a value for the $($from.tolower()) unit.
.Parameter Round
Round the result to this many decimal places. Specify a value between 0 and 10.
.Parameter Detailed
Get a rich object result.
.Example
PS C:\> $cmd 1
$ExampleValue

Get a value result.
.Example
PS C:\> $cmd 1 -detailed

$($obj.trim())

Get an object result.
.Example
PS C:\> $alias 1
$ExampleValue

Using the function alias.
.Link
Convert-$($To)To$($From)
.Notes
This command has an alias of $alias. This is an auto-generated function.
.Inputs
System.Double
.Outputs
System.Double
#>
"@
    $h
}

该函数在 New-MetricFunction 中调用。因为我需要样本的实际值,所以我生成一个值并将其传递给帮助函数。

$b = [scriptblock]::create("param (`$value) [math]::Round($code,2)")
$v1 = Invoke-Command $b -ArgumentList 1
$help = New-MetricFunctionHelp -From $From -to $To -Alias $alias -ExampleValue $v1

重新运行我的命令来创建 Convert-GramToOunce 会生成此代码。

<#
.Synopsis
Convert a value from Gram to Ounce.
.Description
Use this function to convert values between Gram and Ounce. The default output
is the converted value. Or you can use -Detailed to get a rich object. See examples.
.Parameter Value
Specify a value for the gram unit.
.Parameter Round
Round the result to this many decimal places. Specify a value between 0 and 10.
.Parameter Detailed
Get a rich object result.
.Example
PS C:\> Convert-GramToOunce 1
0.04

Get a value result.
.Example
PS C:\> Convert-GramToOunce 1 -detailed

Gram Ounce
---- -----
   1  0.04

Get an object result.
.Example
PS C:\> gr2oz 1
0.04

Using the function alias.
.Link
Convert-OunceToGram
.Notes
This command has an alias of gr2oz. This is an auto-generated function.
.Inputs
System.Double
.Outputs
System.Double
#>
[Cmdletbinding()]
[Alias("gr2oz")]
[OutputType([System.Double])]
Param(
    [Parameter(Position = 0, Mandatory,ValueFromPipeline,HelpMessage = "Specify a gram value.")]
    [ValidateNotNullOrEmpty()]
    
    [double]$Value,
    [Parameter(HelpMessage = "Round the result to this many decimal places. Specify a value between 0 and 10.")]
    [ValidateRange(0,10)]
    [int]$Round = 2,
    [Parameter(HelpMessage = "Get a rich object result.")]
    [switch]$Detailed
    )
    Process {
        Write-Verbose "Converting $value from gram to ounce rounded to $round decimal places."
        $r = [math]::Round(($value/28.35),$round)
        if ($Detailed) {
            [PSCustomObject]@{
                Gram = $Value
                Ounce = $r
            }
        }
        else {
            $r
        }
    }

从数据生成

还在我这儿?这是完整的元脚本函数。

Function New-MetricFunction {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$From,
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$To,
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$Code,
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Validation
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        #hash table of unit abbreviations used to construct aliases
        $abbreviations = @{
            gram       = "gr"
            ounce      = "oz"
            meter      = "m"
            feet       = "ft"
            millimeter = "mm"
            inch       = "in"
            mile       = "mi"
            kilometer  = "km"
            kilogram   = "kg"
            pound      = "lb"
            fahrenheit = "f"
            celsius    = "c"
            yard       = "yd"
            liter      = "l"
            quart      = "qt"
            milliliter = "ml"
            kelvin     = "k"
        }
    } #begin
    Process {
        #make sure From and To are in proper case
        $from = (Get-Culture).TextInfo.ToTitleCase($from.toLower())
        $to = (Get-Culture).TextInfo.ToTitleCase($to.toLower())
        #define a function name that will be found in the global scope
        $name = "global:Convert-$($from)To$($To)"
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating function Convert-$($from)To$($To)"

        #construct an alias from the data
        $alias = "{0}2{1}" -f $abbreviations[$from], $abbreviations[$to]

        #build comment-based help
        $b = [scriptblock]::create("param (`$value) [math]::Round($code,2)")
        $v1 = Invoke-Command $b -ArgumentList 1
        $help = New-MetricFunctionHelp -From $From -to $To -Alias $alias -ExampleValue $v1

        # this here string will be turned into the function's scriptblock. I need to be careful
        # to escape $ where I want it to remain a variable in the output.
        $body = @"
$help
[Cmdletbinding()]
[Alias("$alias")]
[OutputType([System.Double])]
Param(
    [Parameter(Position = 0, Mandatory,ValueFromPipeline,HelpMessage = "Specify a $($from.tolower()) value.")]
    [ValidateNotNullOrEmpty()]
    $(If ($Validation) {
        $v = "[$validation]"
        $v
    })
    [double]`$Value,
    [Parameter(HelpMessage = "Round the result to this many decimal places. Specify a value between 0 and 10.")]
    [ValidateRange(0,10)]
    [int]`$Round = 2,
    [Parameter(HelpMessage = "Get a rich object result.")]
    [switch]`$Detailed
    )
    Process {
        Write-Verbose "Converting `$value from $($from.tolower()) to $($to.ToLower()) rounded to `$round decimal places."
        `$r = [math]::Round(($code),`$round)
        if (`$Detailed) {
            [PSCustomObject]@{
                $From = `$Value
                $To = `$r
            }
        }
        else {
            `$r
        }
    }
"@
        $value = [scriptblock]::Create($body)

        if ($PSCmdlet.ShouldProcess($name)) {
            New-Item -Path function: -Name $name -Value $value -Force
        }
        if ($PSCmdlet.ShouldProcess($name, "Create alias $alias")) {
            #need to manually create the alias even though it is defined in the function
            Set-Alias -Name $alias -Value $name -Scope Global
        }
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

该函数还允许我为该值指定一个参数验证字符串。另请注意,我将参数配置为按属性名称接受管道输入。

我可以使用外部数据源(例如 CSV 文件)来生成所有函数。在具有先前定义的函数的脚本文件中,我像这样定义 CSV 数据。

#use single quote for the here-string so that $value doesn't get
#treated as a variable upon conversion
$data = @'
From,To,Code,Validation
Gram,Ounce,$value/28.35,ValidateScript({$_ -gt 0})
Ounce,Gram,$value*28.35,ValidateScript({$_ -gt 0})
Millimeter,Inch,$value/25.4,ValidateScript({$_ -gt 0})
Inch,Millimeter,$value*25.4,ValidateScript({$_ -gt 0})
Meter,Feet,$value*3.281,ValidateScript({$_ -gt 0})
Feet,Meter,$value/3.281,ValidateScript({$_ -gt 0})
Kilometer,Mile,$value/1.609,ValidateScript({$_ -gt 0})
Mile,Kilometer,$value*1.609,ValidateScript({$_ -gt 0})
Kilogram,Pound,$value*2.205,ValidateScript({$_ -gt 0})
Pound,Kilogram,$value/2.205,ValidateScript({$_ -gt 0})
Yard,Meter,$value/1.094,ValidateScript({$_ -gt 0})
Meter,Yard,$value*1.094,ValidateScript({$_ -gt 0})
Liter,Quart,$value*1.057,ValidateScript({$_ -gt 0})
Quart,Liter,$value/1.057,ValidateScript({$_ -gt 0})
Milliliter,Ounce,$value/29.574,ValidateScript({$_ -gt 0})
Ounce,Milliliter,$value*29.574,ValidateScript({$_ -gt 0})
Celsius,Fahrenheit,($value*(9/5)) + 32
Fahrenheit,Celsius,($value - 32)*(5/9)
Kelvin,Fahrenheit,($value-273.15)*(9/5)+32
Fahrenheit,Kelvin,($value-32)*(5/9)+273.15
Kelvin,Celsius,($value - 273.15)
Celsius,Kelvin,($value + 273.15)
'@

导入此数据并将其传递给元脚本函数是一个单行命令。

$new = $data | ConvertFrom-Csv | New-MetricFunction

最后,为了让我更容易记住所有新命令,我将创建一个“备忘单”。

$metric = $new | Select-Object -Property Name, @{Name = "Alias"; Expression = { Get-Alias -Definition "global:$($_.name)" } }
$metric

假设我对 PowerShell 脚本文件进行点源,我将有一个引用变量。

[玩转系统] 度量元 PowerShell 脚本

通过一个命令,我创建了所有这些函数并将它们加载到我的 PowerShell 会话中。

[玩转系统] 度量元 PowerShell 脚本

[玩转系统] 度量元 PowerShell 脚本

如果我决定更改某些内容(例如详细消息),我可以修改 New-MetricFunction 并重新生成函数。我不必在 22 个不同的文件中进行更改。

概括

虽然我重视结果并将使用这些度量转换函数,但我的真正目的是共享元脚本内容和技术。我心里没有其他项目要使用它,但当我这样做时我会做好更好的准备。如果您找到使用这些技术的方法,我希望您能分享。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯