不能在函数中调用管道属性。电源外壳

Can't call piped properties in a function. Powershell

所以我正在尝试创建一个“下载”函数,它使用管道对象 属性 来确定下载方法(sftp 或 http)。然后为 putty/winscp 或 curl the http url 创建一个 sftp 脚本。我定义对象如下:

#WinSCP
$winscp = new-object psobject            
$winscp | add-member noteproperty name "WinSCP"
$winscp | add-member noteproperty dltype "http"
$winscp | add-member noteproperty file "winscp.exe"
$winscp | add-member noteproperty url "https://cdn.winscp.net/files/WinSCP-5.17.8-Setup.exe"
$winscp | add-member noteproperty path "$env:ProgramFiles(x86)\WinSCP"
$winscp | add-member noteproperty install 'msiexec /i "$DataPath$winscp.file" /quiet /norestart'

#Database
$db = new-object psobject            
$db | add-member noteproperty name "Client Database"
$db | add-member noteproperty dltype "sftp"
$db | add-member noteproperty file "database_"
$db | add-member noteproperty ver "check"
$db | add-member noteproperty ext ".csv"
$db | add-member noteproperty dir "db"

#DatabaseVersion
$db_ver = new-object psobject            
$db_ver | add-member noteproperty name "Database Version File"
$db_ver | add-member noteproperty dltype "sftp"
$db_ver | add-member noteproperty file "current_version.txt"
$db_ver | add-member noteproperty dir "db"

目前,函数中的 $Input 变量有问题。它只能使用一次并且不会转换为 if 语句。由于它包含一个具有多个属性的对象,因此我认为首先需要在函数内将其转换为一个新对象。我是 powershell 的新手,还没有找到这样做的方法。这是我制作并尝试使用的功能:

function Download () {

    #HTTP Download Method
    if ($input.dltype -eq "http") {
        curl $input.url -O $DataPath$input.file
        #HTTP Success or Error
        $curlResult = $LastExitCode
        if ($curlResult -eq 0)
        {
          Write-Host "Successfully downloaded $input.name"
        }
        else
        {
          Write-Host "Error downloading $input.name"
        }
        pause
}

    #SFTP Download Method
    if ($input.dltype -eq "sftp") {
        sftpPassCheck
        #Detect if version required
        if ($input.ver = "check") {
        #Download the objects version file
        "$+$Input+_ver" | Download
        #Update the object's ver property
        $input.ver = [IO.File]::ReadAllText("$DataPath\current_version.txt")
        #Build the new filename
        $input.file = "$input.file"+"$input.ver"+"$input.ext"
        #Delete the version file
        Remove-Item "$DataPath\current_version.txt"
        }
        & "C:\Program Files (x86)\WinSCP\WinSCP.com" `
         /log="$DataPath\SFTP.log" /ini=nul `
         /command `
            "open sftp://ftpconnector:$script:sftp_pass@$input.ip/ -hostkey=`"`"ssh-ed25519 255 SETvoRlAT0/eJJpRhRRpBO5vLfrhm5L1mRrMkOiPS70=`"`" -rawsettings ProxyPort=0" `
            "cd /$input.dir" `
            "lcd $DataPath" `
            "get $input.file" `
            "exit"
        #SFTP Success or Error
        $winscpResult = $LastExitCode
        if ($winscpResult -eq 0)
        {
          Write-Host "Successfully downloaded $input.name"
        }
        else
        {
          Write-Host "Error downloading $input.name"
        }
    }
}

我可能遗漏了一些简单的东西,但我现在一无所知。哦用法应该是:

WinSCP | download

将来自管道的输入绑定到函数参数的正确方法是声明一个高级函数 - 请参阅 about_Functions_Advanced_Parameters 和此答案底部的实施。

然而,在简单的情况下,filter 会执行 ,这是一种函数的简化形式,它将管道输入隐式绑定到 automatic $_ variable 和为每个输入对象调用:

filter Download {
  if ($_.dltype -eq "http") { 
    # ...
  }
}

$input is another automatic variable,在 simple(非高级)functions 中是一个 enumerator 对于 所有 管道输入 正在接收,因此必须 循环 .

即下面的简单function相当于上面的filter:

function Download {
  # Explicit looping over $input is required.
  foreach ($obj in $input) {
    if ($obj.dltype -eq "http") { 
      # ...
    }
  }
}

如果你想把它变成一个高级函数(注意我已经改变了名字以符合PowerShell的动词-名词命名约定):

function Invoke-Download {

  param(
    # Declare a parameter explicitly and mark it as
    # as pipeline-binding.
    [Parameter(ValueFromPipeline, Mandatory)]
    $InputObject  # Not type-constraining the parameter implies [object]
  )

  # The `process` block is called for each pipeline input object
  # with $InputObject referencing the object at hand.
  process {

    if ($InputObject.dltype -eq "http") { 
      # ...
    }

  }

}

- $input 并不是真的要直接使用,你可能最好明确声明你的输入参数!

除了该答案中显示的 $InputObject 模式之外,您还可以按名称将输入对象 属性 值绑定到参数:

function Download
{
  param(
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [Alias('dltype')]
    [string]$Protocol = 'http'
  ) 
  process {
    Write-Host "Choice of protocol: $Protocol"
  }
}

注意虽然这个参数的名字是$Protocol,但是[Alias('dltype')]属性会保证输入对象上dltype属性的值是绑定的.

这样的效果是:

PS ~> $WinSCP,$db |Download
Choice of protocol: http
Choice of protocol: sftp

对任何必需的输入参数重复此模式 - 声明一个映射到 属性 名称的命名参数(如有必要),您可能会得到如下结果:

function Download
{
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateSet('sftp', 'http')]
        [Alias('dltype')]
        [string]$Protocol,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('dir')]
        [string]$Path = $PWD,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('url','file')]
        [string]$Uri
    )

    process {
        Write-Host "Downloading $Uri to $Path over $Protocol"
    }
}

现在你可以做:

PS ~> $WinSCP,$db |Download
Downloading https://cdn.winscp.net/files/WinSCP-5.17.8-Setup.exe to C:\Program Files(x86)\WinSCP over http
Downloading database_ to db over sftp

我们不再依赖直接访问 $input$InputObject$_,非常干净。

有关参数声明的详细信息,请参阅about_Functions_Advanced_Parameters help file