特定の文字を1回だけ含むファイルから行を削除したいのですが、その行が2回以上存在するか存在しない場合は、その行をファイルに残します。
例えば:
DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC
ここで、削除したい文字はC
なので、FGTHDC
が1回だけあるため、コマンドはJUTDYC
およびC
の行を削除する必要があります。
sed
またはawk
を使用してこれを行うにはどうすればよいですか?
awk
では、フィールドセパレーターを任意に設定できます。これをC
に設定すると、C
と同じ数だけフィールドに+1されます。
したがって、awk -F'C' '{print NF}' <<< "C1C2C3"
取得します4
:CCC
は3つのC
s、つまり4つのフィールドで構成されます。
C
が1回だけ出現する行を削除したい。これを考慮して、あなたのケースでは、ちょうど2つのC
フィールドがある行を削除する必要があります。スキップしてください:
$ awk -F'C' 'NF!=2' file
DTHGTY
HYTRHD
HTCCYD
sedアプローチ:
sed -i '/^[^C]*C[^C]*$/d' input
-i
オプションを使用すると、ファイルをその場で変更できます
/^[^C]*C[^C]*$/
-C
を1回だけ含む行に一致します
d
-一致した行を削除します
これは、sed
を使用して次のように実行できます。
コード:
sed '/C.*C/p;/C/d' file1
結果:
DTHGTY
HYTRHD
HTCCYD
方法?
/C.*C/p
を介してC
の少なくとも2つのコピーを含む行を照合して印刷します/C/d
を介してC
を含む行を削除します。これには、手順1ですでに印刷された行が含まれますこれにより、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 ''
のような引数が必要です。)
ファイルのスクリプト編集用の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
のようなものを使用する理由はありますか?
回答:はい。
printf
とecho
では、移植性の問題です。参照 echoよりprintfが優れている理由 そして、printf
を使用してコマンド間に改行を挿入することも簡単です。
printf ... | ex
とex -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
を使用して単一引用符で改行を埋め込むのではなく、コマンド。
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'
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
スイッチを追加して、「インプレース」で編集できます。
特にawk
を必要とする人のために、私は
awk '/C[^C]*C/{next}//{print}'
パターンと一致する場合はその行をスキップし、そうでない場合は印刷します。実際には{print}
は必要ありません。//
とデフォルトの印刷を使用できますが、スペルがはっきりしていると思います。
私の最初の考えは同じパターンでegrep -v
を使用することでしたが、それは実際に提示された質問に答えるものではありません。