Set-Clipboard 仅在快速连续调用时记住最后一个值

Set-Clipboard only remembering the last value when called in rapid succession

我正在尝试创建一个 powershell 片段,它将多行管道输入的第一列复制到剪贴板。

预期用途是:kubectl get pods | copyfirst
这应该允许我在剪贴板中拥有所有 pod 名称,并使用 Win+V select 我需要的单个 pod 名称。

我目前拥有的是:

function copyfirst {
    [CmdletBinding()]Param([Parameter(ValueFromPipeline)]$Param)
    process {
        $Param.Split(" ")[0] | Set-Clipboard
    }
}

问题是 - 这只会将最后一个条目复制到剪贴板,而忽略所有其他条目。

如果我将 Set-Clipboard 更改为其他命令 - 它会按预期工作。例如 echo 输出所有 pod 名称,而不仅仅是最后一个。

注:

  • 此答案显示了如何将 所有 输入行中第一个以空格分隔的字段作为单个剪贴板条目复制.

  • 事实证明,OP 的目的是将每个这样的字段单独复制到剪贴板,以创建一系列可以通过以下方式调用的条目Windows 10 剪贴板历史功能 (WinKey+V), 提供了解决方案。


要从所有个输入对象中复制数据,您必须在process块中收集它并在end块中将集合复制到剪贴板,在处理完所有输入对象后:

function copyfirst {
    [CmdletBinding()]Param([Parameter(ValueFromPipeline)]$Param)
    begin {
        # Initialize the collection (list) that will collect data.
        $coll = [System.Collections.Generic.List[object]]::new()
    }
    process {
        # Add to the collection.
        $coll.Add($Param.Split(" ")[0])
    }
    end {
        # Copy the collection to the clipboard.
        # More efficient alternative:
        #    Set-Clipboard $coll
        $coll | Set-Clipboard
    }
}

process块为每个输入对象调用,并且Set-Clipboard总是替换之前的内容在剪贴板上,这解释了为什么只有 last 数据项被放在那里。

请注意,来自外部程序的标准输出通过管道逐行传递。


请注意,在您的情况下,有一个更简单的替代方案,使用 简单(非advanced) function and the automatic $input variable:

function copyfirst {
  $input | ForEach-Object { $_.Split(' ')[0] } | Set-Clipboard
}

这种方法的潜在缺点是丢失了管道的 方面(逐个处理),因为简单函数的主体就像隐式 end 块,其中 PowerShell 通过 $input.

提供预先收集的输入对象

但是,在这种情况下,这并不重要,因为无论如何您都需要收集所有数据。

您可以使用 .ForEach() array method instead of the ForEach-Object cmdlet 稍微加快操作速度,但我怀疑它在这种情况下是否重要。

我发现,当在同一进程中使用 set-clipboard 时,剪贴板将被覆盖。有一个 -append 但它附加到最后一个剪贴板 value 而不是添加到剪贴板列表。

我最终做的是构建一个命令并在新进程(这很慢!)下为数组中的每个项目执行它,如下所示:

function copyfirst {
    [CmdletBinding()]Param([Parameter(ValueFromPipeline)]$Param)
    
    foreach($line in $Param.split("`n")){
        $podName = $line.split(" ")[0]
        if ($podName -ne "NAME"){
            $scriptBlock = "Write-Output `"$podName`"  | Set-Clipboard; Start-sleep -milliseconds 1"
            Powershell $scriptBlock
        }
    }
}


"NAME READY STATUS RESTARTS AGE 
redis-fd794cd65-k9mhp 1/1 Running 0 3h15m
website-restrictor-c6f5bbd56-fxb59 1/1 Running 0 3h15" | copyfirst

这会导致使用 Windows Key + V

时看到的单个剪贴板条目

请注意 Start-sleep -milliseconds 1 这是因为在非常快速地执行 set-clipboard cmdlet 时出现了一些奇怪的行为,此处已注明 - https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-clipboard?view=powershell-7.1#notes

我认为 是正确的开始,我个人并不知道这个 Win + V 剪贴板功能。所以,你是对的,因为它似乎无法在快速连续完成时捕获历史记录。

通过添加 Start-Sleep 它工作正常:

function copyfirst {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [object[]] $Param
    )

    process {
        $Param.Split(' ')[0] | Set-Clipboard
        Start-Sleep -Milliseconds 250
    }
}

@'
string1 string4
string2 string5
string3 string6
'@ -split '\r?\n' | copyfirst

它应该捕获 string1string2string3

调整睡眠计时器,直到它不会太慢并且它可以捕获所有内容。


经过一些测试,似乎 Start-Sleep 可以减少到 -Milliseconds 250,低于它会产生不一致的结果。