文字が1回だけ含まれている場合に行を削除する方法


10

特定の文字を1回だけ含むファイルから行を削除したいのですが、その行が複数回存在する場合や存在しない場合は、その行をファイルに残してください。

例えば:

DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC

ここで、私は削除したい文字があるCので、このコマンドは行を削除する必要がありますFGTHDCし、JUTDYC彼らが持っているので、C正確に一度だけ。

sedまたはを使用してこれを行うにはどうすればよいawkですか?

回答:


20

ではawk、フィールドセパレータを任意に設定できます。これをに設定するとC、の出現と同じ数のフィールドに+1ができますC

だからあなたawk -F'C' '{print NF}' <<< "C1C2C3"が得ると言うなら4CCCは3 C秒で構成されるため、4つのフィールドになります。

C一度だけ発生する行を削除したい。これを考慮して、あなたのケースでは、ちょうど2つのCフィールドがある行を削除する必要があります。だからそれらをスキップしてください:

$ awk -F'C' 'NF!=2' file
DTHGTY
HYTRHD
HTCCYD

4
awkフィールドセパレーターの使用を正確に!
バレンティンB.17年

インターレスト、デフォルトの場合(FS = "")のように、先頭のスペース($ 1 =行の最初の非スペース)と繰り返しも無視します(フィールド1とフィールド2を区切るために5つのスペースを使用できます)...スペースおそらく特別に扱われますか?(それを見るには、awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'いくつかの行を実行し、複数のspcesを含むものや、スペースで始まるものを実行してフィードすることができます)
Olivier Dulac

2
@OlivierDulac、はい、スペースはPOSIXで指定されているように特別に処理されます
ワイルドカード2017年

8

sedアプローチ:

sed -i '/^[^C]*C[^C]*$/d' input

-i オプションにより、ファイルのインプレース変更が可能

/^[^C]*C[^C]*$/-1 C回だけ含む行に一致します

d -一致した行を削除


8

これは次のようにして行うことができますsed

コード:

sed '/C.*C/p;/C/d' file1

結果:

DTHGTY
HYTRHD
HTCCYD

どうやって?

  1. 少なくとも2つのCviaのコピーを含む行を一致させて印刷する/C.*C/p
  2. Cビアのある行を削除します/C/d。これには、手順1ですでに印刷された行が含まれます。
  3. デフォルトで残りの行を印刷する

2
賢い代替アプローチ; 私はそれが好きです。
ワイルドカード2017年

6

これにより、Cが1つだけ出現する行が削除されます。

grep -v '^[^C]*C[^C]*$' file

正規表現[^C]は、C(または改行)ではない1文字に一致し、繰り返し演算子(別名Kleeneスター)*は、前の式の0回以上の繰り返しを指定します。

grep(および他のほとんどのテキスト指向ツール)からのデフォルトの出力は、標準出力です。新しいファイルにリダイレクトし、必要に応じて元のファイルの上に移動することもできます。同じ正規表現sed -iをインプレース編集に使用できます。

sed -i '/^[^C]*C[^C]*$/d' file

(一部のプラットフォーム、特にmacOSを含む* BSDでは、-iオプションにはなどの引数が必要-i ''です。)


1
sed -i '/^[^C]*C[^C]*$/d' file-以前に投稿されたように聞こえますが、どのように考えますか、盗作?
RomanPerekhrest

1
確かに、いくつかの重複があります。私はgrep答えから始めましたが、明らかにsed -iバリアントに簡単に拡張できます。以前のgrep答えを探していたので、あなたの答えは見つかりませんでした。
Tripleee

1
ユーティリティがエラーなしで終了した場合は、単にを避け-ised代わりに新しいファイルにリダイレクトし、元のファイルをそのファイルに置き換える方が安全sedです。
クサラナンダ

2
それともgrep -vx '[^C]*C[^C]*'
ステファンChazelas

@Kusalanandaしかし、grepより明確で堅牢であるため、使用することもできます(特に、sed有益な終了コードが少ないため)。
Tripleee

4

ファイルのスクリプト編集用のPOSIXツールは(変更された内容を標準出力に出力するのではなく)ですex

printf '%s\n' 'g/^[^C]*C[^C]*$/d' x | ex file.txt

もちろん、Sedのバージョンがそれをサポートしている場合は使用sed -iできますが、さまざまなタイプのシステムで実行することを目的としたスクリプトを作成している場合は移植できないことに注意してください。


David Foersterがコメントで尋ねました:

あなたが使っている理由はありprintfませんechoex -c COMMAND

回答:はい。

printf比較しechoて、それは移植性の問題です。printfがechoよりも優れている理由を参照してください また、を使用してコマンド間に改行を挿入するのも簡単printfです。

以下のためにprintf ... | exex -c ...には、エラー処理の問題です。この特定のコマンドでは重要ではありませんが、通常は重要です。たとえば、入れてみてください

ex -c '%s/this pattern is not in the file/replacement text/g | x' filename

スクリプトで。次とは対照的です。

printf '%s\n' '%s/no matching lines/replacement/g' x | ex file

最初はハングし、入力を待ちます。2番目は、exコマンドがEOFを受信すると終了するため、スクリプトは続行されます。などの代替回避策s///eがありますが、POSIXでは指定されていません。私は、上に示したポータブルフォームを使用することを好みます。

以下のためgのコマンド、そこになければなりません最後に改行すること、そして私が使って好みprintfのコマンドをラップするのではなく、単一引用符での改行を埋め込みます。


1
あなたが使っている理由はありprintfませんechoex -c COMMAND
デビッドフォースター

@DavidFoerster、はい。私はコメントであなたに答え始めました、それは長くなりました、それで私はそれを答えに加えました。
ワイルドカード2017年

ありがとう、+ 1!printfvs については知っていましたecho(ただしecho、引数がハードコーディングされている場合は、たいてい好んで使用exします)が、これまで広範囲に使用したことはありません。
デビッドFoerster

2

ここでは、perlを使用するいくつかのオプションを示します。

一致するのは単一の文字だけなので、tr/C//(置換なしの翻訳)を使用して、の一致数を返すことができますC

perl -lne 'print if tr/C// != 1' file

より一般的には、複数文字の文字列または正規表現に一致させたい場合は、これを使用できます。

perl -lne 'print if (@m = /C/g) != 1' file

これにより、正規表現の一致が/C/gリストに割り当てられ、@mそのリストの長さがでない場合に行が出力され1ます。

-iスイッチは「インプレース」編集に追加することができます。


2
sed -e '
  s/C/&/2;t   # when 2nd C matches skip processing and print
  /C/d        # either one C or no C, so delete on C
'

sed -e '
   /C/!b     # no C, skip processing and print
   /C.*C/!d  # not(at least 2 C) => 1 C => delete
'

perl -lne 's/C/C/g == 1 or print'

GNUを想定していることに注意してください。通常sed、他のほとんどの実装でt #...呼び出されるラベルに分岐します。#...sed
ステファンChazelas

!bブランチもラベルまたはその後の改行以外は好きではないので、GNU でもsedです。

はい、bt:}(とr filew file...)同じ行にそれらの後にコマンドを持つことができません。別の-eオプションを使用することもできます。
ステファンChazelas

あなたのperlオプションは正しい出力を生成しません。g修飾子を追加するのを忘れたようです。
トムフェネック2017年

@TomFenechあなたは正しいです。私はそれを修正しています。ありがとう。

1

特に欲しい人のためにawk、私は提供します

awk '/C[^C]*C/{next}//{print}'

パターンと一致する場合はその行をスキップし、そうでない場合は印刷します。実際には必要ありませんが{print}//デフォルトの印刷を使用できますが、スペルがはっきりしていると思います。

私の最初の考えはegrep -v同じパターンで使用することでしたが、それは実際に提起された質問に答えるものではありません。


1
あとで何かを照合する意味は何{next}ですか?言うだけでawk '/pattern/ {next} 1'、パターンに一致しないすべての行が印刷されます。または、awk '!/pattern/'それらを直接印刷することをお勧めします。
fedorqui

@fedorquiの良い点!/pattern/(それはどういうわけか私の心を滑らせた)ですが、私はむしろ//{print}不可解なものよりも自明であると思い1ます。次の担当者がコードを保守する能力が最も低く、流暢であることを想定し、コードの効率や効果を大幅に低下させないようにします。
nigel222
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.