1

頻繁に使用する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($. $_)" %*

しかし、cmd.exeとbatファイルの一般的な醜さを呼び出さなければならない(CTRL-C :-()を押したときに「バッチジョブの終了(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($. $_)'ます。

4

2 に答える 2

5

基本:高度な機能は標準機能以上のことを実行できます

双方向で機能するように関数を作成する方法はありますか?

はい、高度な関数を使用して、プロセスブロックが入力オブジェクトごとに呼び出されます。

  • ブロック[CmdletBinding]の直前の開始時に指定する必要があります。param
  • パイプライン入力を受け取るパラメーターが必要です。これは、Parameterそのパラメーターの属性を使用して行われます。

このような:

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

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

ネイティブに物事を行う方が良い

head -1(ファイルの最初の行を印刷します)

Firstのパラメータを見てくださいSelect-Object... | Select -f 1 | ....最初のオブジェクトを通過させるだけです。

sed $ d(ファイルの最後の行を削除します)

これはもっと難しいです...本質的に別の行がある場合に追跡する機能。

perl -pe'$ _ = qq($。$ _)'(ファイルの数直線)

が必要ですMeasure-Object。これは、他のパラメーターがないと、パイプラインで受け取るオブジェクトの数をカウントします。


なぜ機能しないのか

(拡張された質問に基づく)

これには2つの部分があります。

まず、パイプラインにバインドされたパラメーターの値を操作に渡す必要があります。それで:

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

する必要があります

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

2番目:これはおそらく多くのユーティリティでは機能しません。ブロックが実行されるたびに、パイプライン上の各オブジェクトの(など)processの新しい呼び出しを含むパイプラインの新しいインスタンスが実行されるため、sedある行から次の行まで維持することが通常期待される状態。

この周りには2つのルートがあります。まず、高度なトピックであるステップ可能なパイプラインを使用できます(そして、適切な報道は、ブルース・ペイエット(PSH言語の設計と実装のほとんどを行った)による 『 Windows PowerShell in Action Second Edition』にあります)。

第二に:ネイティブに物事を行います。例えば。ファイルの行数(を使用しない場合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タイプのツールの柔軟性を失い(PSHの実行モデルは異なります。1つのプロセスでツールを連携させる、 PSHの柔軟性(PSHは文字列ではなく型付きオブジェクトで機能します)。

于 2013-01-30T15:38:30.143 に答える
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
于 2013-02-01T10:44:37.670 に答える