我有许多我经常使用的 sed/perl/etc “单行”命令:
head -1
(打印文件的第一行)sed $d
(删除文件的最后一行)perl -pe '$_ = qq($. $_)'
(文件中的行数)你明白了。
所有这些命令都有相同的行为——它们可以从标准输入中获取输入,或者处理一系列名称作为参数传递的文件。我想将这些常用脚本包装为 Powershell 函数,这样我就不必记住要使用的确切语法。但是,别名不是那样工作的,如果我用函数做“明显”的方法:
function numlines {
perl -pe '$_ = qq($. $_)' $args
}
它适用于文件作为参数(
numlines my_file.pl
),但不适用于来自管道的输入( cat my_file.pl | numlines
)。有没有办法编写函数以使其双向工作?
澄清一下 - 我可以使用 bat 文件来做到这一点。例如, numlines.bat 包含
@perl -pe "$_ = qq($. $_)" %*
但是必须调用 cmd.exe 和 bat 文件的一般丑陋(即“终止批处理作业(Y/N)?”当您按下 CTRL-C :-() 时的提示让我希望在 Powershell 中有一个类似简单的解决方案。 .
根据下面理查德的建议,我尝试了:
function test {
[CmdletBinding()]
param(
[Parameter(mandatory=$true, ValueFromPipeline=$true)]
$data
)
process {
perl -pe '$_ = qq($. $_)'
}
}
如果我这样做
test file.txt
(我希望以与 perl -pe '$_ = qq($. $_)' file.txt
相同的方式有效运行)该函数运行,但等待标准输入上的数据而不是处理 file.txt
.当我尝试 cat file.txt | test
时也会发生同样的事情- 我希望其行为与 cat file.txt | perl -pe '$_ = qq($. $_)'
相同.
最佳答案
基础知识:高级功能可以做的比标准功能更多
Is there a way to write the function so that it works both ways?
是的,使用 advanced functions ,将为每个输入对象调用进程 block 。
[CmdletBinding]
在 param
之前的开始处堵塞。 Parameter
完成的。该参数的属性。 像这样:
function ReadInput {
[CmdletBinding]
param(
[Parameter(mandatory=$true, ValueFromPipeline=$true)]
$data
)
process {
"Input was: $data";
}
}
更好地在本地做事
head -1 (Print the first line of a file)
看
First
Select-Object
的参数: ... | Select -f 1 | ....
只会通过第一个对象。sed $d (Drop the last line of a file)
这个更难......本质上是一个跟踪是否有另一行的函数。
perl -pe '$_ = qq($. $_)' (Number lines in a file)
您需要
Measure-Object
,如果没有其他参数,它将计算它在管道上接收到的对象的数量。为什么它不起作用
(基于扩展问题)
这有两个部分:
首先:您需要将绑定(bind)到管道的参数的值传递给您的操作。所以:
process { perl -pe '$_ = qq($. $_)' }
应该
process {
$data | perl -pe '$_ = qq($. $_)'
}
第二:这可能不适用于您的许多实用程序,因为每次 process
block 被执行 管道的一个新实例将被执行,包括一个新的 sed
的调用(等)对于管道上的每个对象,从而失去您通常期望它从一行到下一行保持的任何状态。这周围有两条路线。首先,您可以使用可步进的管道,这是一个高级主题(唯一体面的介绍是在 Bruce Payette(他完成了大部分 PSH 语言的设计和实现)的 Windows PowerShell in Action 第二版一书中)。
第二:做事原生。例如。文件中的行数(不使用
Measure-Object
):function Get-ObjectCount {
[CmdletBinding]
param(
[Parameter(mandatory=$true, ValueFromPipeline=$true)]
[object[]]$data # Accept an array...
)
begin {
$count = 0 # Not strictly needed: PSH will default this.
}
process {
$count += $data.length
}
end {
$count
}
}
这也会快得多(无需创建另一个进程。只要您专注于将 PSH 作为包装器,您就会发现您正在两全其美:失去 *ix 类型工具的灵 active (PSH 的执行模型不同:在一个进程中协作工具)并失去PSH 的灵 active (PSH 适用于类型化对象而不是字符串)。
关于powershell - 如何编写包装 sed/perl/etc 命令的 powershell 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14607249/