如何编写包含sed / perl / etc命令的powershell函数

时间:2013-01-30 15:13:33

标签: powershell

我经常使用一些sed / perl / etc“one-liner”命令:

  • 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($. $_)" %*

但是当你点击CTRL-C时,不得不调用cmd.exe和bat文件的一般丑陋(“终止批处理作业(Y / N)?”提示),这让我想要一个类似的简单解决方案的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($. $_)'完全相同。

2 个答案:

答案 0 :(得分:5)

基础:高级功能可以做多于标准功能

  

有没有办法编写函数以便它可以双向工作?

是的,使用advanced functions,将为每个输入对象调用过程块。

  • 您需要在param阻止之前的开头指定[CmdletBinding]
  • 您需要一个接受管道输入的参数,这是通过该参数的Parameter属性完成的。

像这样:

function ReadInput {
  [CmdletBinding]
  param(
    [Parameter(mandatory=$true, ValueFromPipeline=$true)]
    $data
  )

  process {
    "Input was: $data";
  }
}

更好地做事本地

  

head -1(打印文件的第一行)

查看First的{​​{1}}参数:Select-Object只会传递第一个对象。

  

sed $ d(删除文件的最后一行)

这个更难......本质上是一个跟踪是否有另一条线的功能。

  

perl -pe'$ _ = qq($。$ _)'(文件中的数字行)

您需要... | Select -f 1 | ....,没有其他参数将计算它在管道上接收的对象数量。


为什么不工作

(基于扩展的问题)

这有两个部分:

首先:您需要将绑定到管道的参数值传递给您的操作。所以:

Measure-Object

应该是

process {
  perl -pe '$_ = qq($. $_)'
}

第二:这可能不适用于很多实用程序,因为每次执行process { $data | perl -pe '$_ = qq($. $_)' } 块时,都会执行新的管道实例,包括process的新调用(等)对于管道上的每个对象,从而丢失您通常期望从一行到下一行保持的任何状态。

这有两条路线。首先,您可以使用可移植管道,这是一个高级主题(唯一不错的选择是在书中 Windows PowerShell in Action 第二版由Bruce Payette(他做了大部分工作) PSH语言设计和实现))。

第二:本地做事。例如。文件中的行数(不使用sed):

Measure-Object

这也会快得多(不需要创建另一个流程。

只要你专注于PSH作为包装器,你就会发现你是两个世界中最糟糕的:失去了* ix类型工具的灵活性(PSH的执行模型不同:在一个过程中合作工具) 失去了PSH的灵活性(PSH适用于类型对象而不是字符串)。

答案 1 :(得分:0)

解决方案的关键似乎是在函数中使用$ args(用于命令行参数)和$ input(用于管道输入),如下所示:

function wrapper {
    $input | WRAPPED_COMMAND_HERE $args
}

因此,例如,对数字行的perl命令的情况如下所示

PS> function nl {
>>    $input | perl -pe '$_ = qq($. $_)' $args
>>  }
>>
PS> nl test.txt
1 This is some
2 test data
PS> type test.txt | nl
1 This is some
2 test data