文字列を順次インデックスに置き換えます


9

誰かがこれを達成するためのエレガントな方法を提案できますか?

入力:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

出力は次のようになります。

test      instant1  ()

test      instant2  ()

test      instant1000()

空の行が入力ファイルにあり、同じディレクトリの下に一度に処理する必要のあるファイルがたくさんあります。

同じディレクトリ内の多くのファイルを置き換えるためにこれを試してみましたが、機能しませんでした。

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

エラー:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

そして私もこれを試しました: perl -i -pe 's/instant/$& . ++$n/ge' *.vs

それは機能しましたが、インデックスは1つのファイルから別のファイルに増加し続けました。差分ファイルの場合、それを1にリセットしたいと思います。何か良い提案はありますか?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

動作しますが、他のすべてのファイルは置き換えられません。ファイルを「* .txt」のみに置き換えることを好みます。


そして、それらはすべて空白行だ​​けで構成されていますかtest instant ()
terdon

二重スペース行を元に戻します。それらは、多くの場合、このサイトのマークアップの使用方法を知らない新しいユーザーの兆候です。そのため、ファイルコンテンツブロックを適切にインデントしながら、terdonがそれらを削除して、ファイルコンテンツとして表示します。今は大丈夫だと思います。
ティモ

回答:


13
perl -pe 's/instant/$& . ++$n/ge'

またはGNUでawk

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

ファイルをその場で編集するには、次の-iオプションを追加しますperl

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*

または再帰的に:

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

解説

perl -pe 's/instant/$& . ++$n/ge'

-p入力を行-eごとに処理し、各行に渡された式を評価して出力します。各行について、それ自体()と変数の増分値を(s/re/repl/flags演算子を使用して)置き換えます。フラグが(だけでなく、1回)グローバル置換を作ることであり、交換がへのPerlコードとして解釈されるように、Eの valuate(ない固定文字列)。instant$&++$nge

1つのperl呼び出しが複数のファイルを処理するインプレース編集の場合$n、各ファイルでリセットする必要があります。代わりに、$n{$ARGV}$ARGV現在処理されているファイルは)を使用します。

awk1は、説明のビットに値します。

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

GNUの機能を使用して、awk任意の文字列(正規表現も含む)のレコードを分離しています。では-vRS=instantレコードセパレータをに設定しinstantます。RTマッチしたものを保持する変数であるRSので、一般的に、instantそれは空の文字列になります最後のレコードを除いては。上記の入力では、レコード($0)およびレコード終了記号(RT)は([$0|RT])です。

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

したがって、最初のレコードを除くすべてのレコードの先頭にインクリメント番号を挿入するだけです。

これは、上記で行うことです。最初のレコードはn空になります。ORS(出力出力のセパレーター)をRTに設定して、をawk 出力しn $0 RTます。これは++n、常にtrue(ゼロ以外の数値)と評価される条件である2番目の式()で実行されます。したがって、(printingの$0 ORS)デフォルトのアクションがすべてのレコードに対して実行されます。



4

sed本当に仕事に最適なツールではありません。より優れたスクリプト機能を備えたものが必要です。ここにいくつかの選択肢があります:

  • perl

    perl -000pe 's/instant/$& . $./e' file 

    -p手段はで与えられているものは何でもスクリプト適用した後に、「すべての行を印刷します」-e-000記録(ライン)ので、「段落モード」をオンに連続した改行で定義されています(\n)文字、これは正しくダブルスペース行を扱うことができます。$&は、最後に一致したパターン$.であり、入力ファイルの現在の行番号です。e中にはs///e私が置換演算子で式を評価することができます。

  • awk(これは、データが示されているとおりであり、3つのスペースで区切られたフィールドがあることを前提としています)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    ここでは、現在の行が空でない場合にのみk変数をインクリメントします。その場合、必要な情報も出力されます。空の行はそのまま印刷されます。k/./

  • さまざまな殻

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 

    ここでは、各入力行が自動的に空白で分割され、各フィールドは次のように保存され$a$bそして$c。次に、ループ内で、空でない$c行ごとに1が追加され、その$a現在の値が2番目のフィールドの隣に出力され$bます。

注:上記のすべてのソリューションは、ファイル内のすべての行が同じ形式であることを前提としています。そうでない場合、@ Stephaneの答えは進むべき道です。


多くのファイルを処理し、現在のディレクトリ内のすべてのファイルに対してこれを実行したい場合、これを使用できます。

for file in ./*; do perl -i -000pe 's/instant/$& . $./e' "$file"; done

気をつける:スペースなしの単純なファイル名を想定して、より複雑なものに対処する必要がある場合は、(仮定のために行くksh93zshまたはbash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -000pe 's/instant/$& . $./e' "$file"
done

Perlスクリプトが機能します。ただし、行が2つのスペースである場合、1つの小さな問題があります。
user3342338 2014

@ user3342338はい、現在の行番号を使用しているので、カウンターが増加します。これは非常に素朴なアプローチであり、Stephaneの方がより堅牢です。空白行がある場合、または表示されている内容と異なる行がある場合、これらは機能しません。
terdon

@ user3342338更新された回答を参照してください。これらはすべて、ダブルスペースファイルで機能するはずです。
terdon

素晴らしい答えと代替方法のオプション!! ありがとう
Madivad 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.