このようなファイルがあります。
12345 X678GHR 0 ADD
23445 HGT6787 1 ADD
12345 X678GHR 0 REM
67894 OIY5678 0 ADD
12345 OIY5678 0 ADD
12345 X678GHR 1 ADD
後で追加して削除した行を削除するには、ファイルの行を比較する必要があります。したがって、出力は次のようになります。
23445 HGT6787 1 ADD
67894 OIY5678 0 ADD
12345 OIY5678 0 ADD
12345 X678GHR 1 ADD
その後、ファイルに追加および削除された履歴は消去されます。
更新:また、2列と3列の間に削除されたレコードが一致することを確認する必要がありました。私の元のファイルでは、区切り文字は空白ではありません。閉じカッコ「)」です。
助けてください。私はUNIXを初めて使用します。
答え1
項目の順序を保証する必要がない場合は、以下を提供してください。
$ cat file
12345)X678GHR)0)ADD
23445)HGT6787)1)ADD
12345)X678GHR)0)REM
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD
次は
$ awk -F ')' '
$NF == "ADD" {lines[$1 FS $2 FS $3] = $0}
$NF == "REM" {delete lines[$1 FS $2 FS $3]}
END {for(i in lines) print lines[i]}
' file
12345)X678GHR)1)ADD
67894)OIY5678)0)ADD
23445)HGT6787)1)ADD
12345)OIY5678)0)ADD
本当に順序を維持する必要がある場合は、ファイルを2回渡すだけです。
$ awk -F ')' '
NR == FNR {if($NF == "REM") rem[$1 FS $2 FS $3]; next}
!($1 FS $2 FS $3 in rem)
' file file
23445)HGT6787)1)ADD
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD
答え2
入力行の順序を維持することが重要な場合、単一の連想配列では十分ではないため(awkやPerlを含むほとんどの言語では、連想配列は本質的に順序付けされていないため)、2つの配列が必要です。
- 入力行のテキストを含む数値インデックスを持つ配列
- ハッシュに一致する最初の配列の行番号を含む連想配列。
これはawkよりもPerlではるかに簡単なので、これを使用します。@lines
最初の配列と%keys
2番目の配列に使用します。
@F
このオプションを使用したときに自動的に生成される自動分割フィールドを含む配列の名前-F
。 $F[0]、$F[1] という点を除いて、awk の $1、$2、$3 などに似ています。 $ F [2]などは$F[-1]
@ Fの最後の要素であり、awkのものとほぼ同じです$NF
。 Perl配列は1ではなく0から始まります。
perl -F'\)' -l -e '
$key = join(")",@F[0..$#F-1]);
if ($F[-1] eq "ADD") {
$lines[$.] = $_; # $. is the line number of the current file
$keys{$key} = $.;
} elsif ($F[-1] eq "REM") {
delete($lines[$keys{$key}]);
delete($keys{$key});
}
if (eof) {
foreach $l (@lines) { print $l if $l; };
@lines = ();
%keys = ();
};' inputfile
出力:
23445)HGT6787)1)ADD
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD
このバージョンのPerlは入力内のすべてのフィールドで動作し、データにフィールド1〜3を使用し、「ADD」または「REM」コマンドにフィールド4を使用するようにハードコードされていません。代わりに、データの最後のフィールドとディレクティブの最後のフィールドを除くすべてのフィールドを使用します。これはawkを使用して行うこともできますが、join()
最後のフィールドを除くすべてのフィールドをリンクするには、関数または少なくとも単純なループを作成する必要があります。
eof()
このPerlバージョンは、各ファイルの終わりを検出し(この機能を使用して)、まだ削除されていない行を印刷した後、両方の配列を消去して複数の入力ファイルを処理できます。つまり、各入力ファイルの終わりにすべてをリセットします。 @steeldriverのawk回答のようなブロックを使用することもできましたが、いつEND {}
スクリプトを少し異なる方法で再利用したいかわからないので、適切なようです...そして常に自分自身に尋ねることをお勧めします。 「もし?」そして、「これはどのように失敗するのですか?」という質問を入力してください。
答え3
与えられた
$ cat input
12345)X678GHR)0)ADD
23445)HGT6787)1)ADD
12345)X678GHR)0)REM
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD
...その後
$ tac input | perl -n -l -e \
's/REM$// ? $hit{"${_}ADD"}=1 : print unless delete $hit{$_}' \
| tac
...生産する
23445)HGT6787)1)ADD
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD
説明する:
- この
tac
コマンドはファイルを最後の行から最初の行に並べ替えるので、一致するADD行の前にREM行が表示されます。もう一方は、tac
最後に元の順序を復元するために使用されます。 - Perlコマンドスイッチは次のように組み合わせることができますが、明確にする
-nle
ために分離されています。-n
=各行を自動的に印刷しない-l
=各入力行の末尾から改行文字を削除し、それをすべてのprint
コマンドに再度追加します。-e
= Perlスクリプトの1行を指定(「入力」)します。
- Perlコードは多くのことをします:
- 演算子
?:
は if/then/else です。condition?expr1:expr2
- 条件が true の場合は expr1 を実行し、そうでない場合は expr2 を実行します。 - 行末で「REM」を正常に削除できる場合(したがって「12345)X678GHR)0」などの結果が出たら、ここに「ADD」を追加して「ヒットリスト」に貼り付けます。
- そうでない場合(ADDがあることを意味します)、「ヒットリスト」にない場合は、レコードが存在するかどうかに関係なく、「ヒットリスト」からレコードを削除します。
- 演算子
「ヒットリスト」は、殺すか削除するアイテムを指す語句用語です。このPerlコードでは、リストではなく(順序付けされていない)ハッシュ(別名連想配列)のキーとして実装されており、迅速な検索が可能です。各キーに関連する値は重要ではありません。ここでは数字1を使用します。 (0 または undef を除く任意の値を使用できます.)
仮定:
- 行全体(すべての列)を比較しています。とは別に最終ADD/REM。
- REMラインを印刷しないことが許可されています(該当するADDラインがなくても)。
- 入れ子になったキャンセルは発生しません。たとえば、ADD/ADD/REM/REM(3行が2行をキャンセルし、4行が1行をキャンセル)は発生しません。