web-dev-qa-db-ja.com

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

特定の文字を1回だけ含むファイルから行を削除したいのですが、その行が2回以上存在するか存在しない場合は、その行をファイルに残します。

例えば:

DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC

ここで、削除したい文字はCなので、FGTHDCが1回だけあるため、コマンドはJUTDYCおよびCの行を削除する必要があります。

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

10
Namz

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

したがって、awk -F'C' '{print NF}' <<< "C1C2C3"取得します4CCCは3つのCs、つまり4つのフィールドで構成されます。

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

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

sedアプローチ:

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

-iオプションを使用すると、ファイルをその場で変更できます

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

d-一致した行を削除します

8
RomanPerekhrest

これは、sedを使用して次のように実行できます。

コード:

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

結果:

DTHGTY
HYTRHD
HTCCYD

方法?

  1. /C.*C/pを介してCの少なくとも2つのコピーを含む行を照合して印刷します
  2. /C/dを介してCを含む行を削除します。これには、手順1ですでに印刷された行が含まれます
  3. デフォルトで残りの行を印刷する
8
Stephen Rauch

これにより、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 ''のような引数が必要です。)

6
tripleee

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

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

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


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

printfではなくechoまたはex -c COMMANDのようなものを使用する理由はありますか?

回答:はい。

printfechoでは、移植性の問題です。参照 echoよりprintfが優れている理由 そして、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番目は、EOFがexコマンドによって受信されたときに終了するため、スクリプトは続行されます。s///eなどの代替回避策がありますが、そうではありません。 POSIXで指定されています。上記のポータブルフォームを使用することをお勧めします。

gコマンドの場合、最後に改行がmustである必要があり、printfを使用して単一引用符で改行を埋め込むのではなく、コマンド。

4
Wildcard
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'
2
user218374

Perlを使用するいくつかのオプションを次に示します。

一致するのは1文字だけなので、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
Tom Fenech

特にawkを必要とする人のために、私は

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

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

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

1
nigel222