次のファイルがあります。
ブラブラ
パターン 2
ブラ
パターン 1
パターン 2
ブラ
ブラ パターン 1 ブラ
ブラー
パターン1
pattern1
最後の一致から最初の項目を含む太字の行全体を削除したいと思いますpattern2
。
誰にもアイデアがありますか?
ありがとうございます!
答え1
これは1つのex
ライナーです。 (ex
前作とスクリプト形式ではそうですvi
。)
printf '%s\n' '$?pattern2?/pattern1/d' x | ex file.txt
保存x
と終了。%p
変更したファイルのみを印刷したい場合は、次のように変更してください。いいえ変更を保存します(テストに適しています)。
$
はファイルの最後の行を表し、は現在の位置から始まり、?pattern2?
逆方向検索の最初の結果を表すアドレス、は行削除コマンドです。pattern2
/pattern1/
d
ex
順方向および逆方向アドレス指定が必要な場合に使用します。
vi
Vimでも同じことを対話的に実行できます。
vim file.txt
その後、入力してください。
:$?pattern2?/pattern1/d
そしてEnterを押します。
その後、保存して:x
Enterを押して終了します。
答え2
これには無差別的なアプローチがあります。データを読み取り、2回繰り返します。最初はパターン2の最後の発生を検索し、2番目はパターン1の最初の発生を検索します。
#!/usr/bin/perl
# usage: perl remove-pattern.pl [file]
use strict;
# reads the contents of the text file completely
# removes end of line character and spurious control-M's
sub load {
my $file = shift;
open my $in, "<", $file or die "unable to open $file : $!";
my @file_contents = <$in>;
foreach ( @file_contents ) {
chomp;
s/\cM//g;
}
return @file_contents;
}
# gets the first file from the command line
# after the perl script
my $ifile = shift;
# read the text file
my @file_contents = &load($ifile);
# set 2 variables for the index into the array
my $p2 = -1;
my $p1 = -1;
# loop through the file contents and find the last
# of pattern2 (could go reverse the data and find the
# first of pattern2
for( my $i = 0;$i < @file_contents; ++$i ) {
if( $file_contents[$i] =~ /pattern2/) {
$p2 = $i
}
}
# start at the location of the last of pattern2
# and find the first of pattern1
for( my $i = $p2; $i < @file_contents; ++$i ) {
if($file_contents[$i] =~ /pattern1/) {
$p1 = $i ;
last;
}
}
# create an output file name
my $ofile = $ifile . ".filtered";
# open the output file for writing
open my $out, ">", $ofile or die "unable to open $ofile : $!";
# loop through the file contents and don't print the index if it matches
# p1. print all others
for( my $i = 0;$i < @file_contents; ++$i ) {
print $out "$file_contents[$i]\n" if ($i != $p1);
}
--- data.txt ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla pattern1 bla
bla
pattern1
上記のPerlスクリプト名が"remove-pattern.pl"の場合、data.txt入力ファイルが与えられたら、次のコマンドを使用して実行されます。 %> perl delete-pattern.pl data.txt
生成された出力ファイル "data.txt.filtered"
--- data.txt.filtered ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla
pattern1
答え3
行の行番号を見つけるには:
lineno=$( nl file | tac | awk '/pattern1/ {last = $1} /pattern2/ {print last; exit}' )
nl
ファイルに行番号を追加し、
tac
行を反転し、
行awk
番号を印刷するために使用されます。最後「モード1」今後これ最初「モード2」。
その後、この行を削除してください。
sed -i "${lineno}d" file
答え4
ファイルを一度だけ転送し、メモリ内の行数を最小限に抑えたい場合は、ステートマシンawk
アプローチを使用できます。これは最短の解決策ではありませんが、簡単に見つけ、読み取り、保守できます。状態名を数字に置き換えることで(潜在的に)より効率的にすることができます。
PATTERN1=pattern1 PATTERN2=pattern2 awk '
BEGIN {
p1 = ENVIRON["PATTERN1"]
p2 = ENVIRON["PATTERN2"]
state = "init"
}
state == "init" {
if ($0 ~ p2) state = "p2_found"
print
next
}
state == "p2_found" {
if ($0 ~ p1) {
state = "p1_found"
p1_line = $0
printf "%s", hold
hold = ""
} else if ($0 ~ p2) {
# we can print the text held since the last p2
printf "%s", hold
hold = $0 RS
} else hold = hold $0 RS
next
}
state == "p1_found" {
if ($0 ~ p2) {
state = "p2_found"
# the line that matched p1 is not discarded
printf "%s\n%s", p1_line, hold;
hold = ""
}
hold = hold $0 RS
}
END {
# here we are not printing p1_line which is how it is discarded
printf "%s", hold
}'
pattern1
(sumに一致する行がないとしますpattern2
。)