パターンが存在しない限り、ファイルからパターンまで行を削除します。

パターンが存在しない限り、ファイルからパターンまで行を削除します。

sed次のようなものを使用するか、次のようにしてawkフィルタを作成しようとします。

  • 与えられたパターンが入力にない場合、入力全体を出力にコピーします。
  • パターンが入力に存在する場合、最初の発生後の行のみが出力にコピーされます。

これは "git clean"フィルタと連携しますが、おそらく重要ではありません。重要な点はこれです必要stdinで入力が提供されるため、フィルタとして実装されます。

sedたとえば、特定のパターンの行を削除する方法を知っています。ただし、どこにも一致するものがない場合は、1,/pattern/d入力全体が削除されます。/pattern/

grep -q一時ファイルを作成し、操作を実行し、入力で何をするかを決定する完全なシェルスクリプトを作成することを想像できます。可能であれば、一時ファイルを作成せずにこれを行うことをお勧めします。 gitが頻繁に呼び出すことができるので、効率的でなければなりません。

答え1

ファイルが大きすぎてメモリに収まらない場合は、Perlを使用してファイルを読み取ることができます。

perl -0777pe 's/.*?PAT[^\n]*\n?//s' file

PAT希望のモードに変更するだけです。たとえば、次の2つの入力ファイルとスキーマがあるとします5

$ cat file
1
2
3
4
5
11
12
13
14
15
$ cat file1 
foo
bar
$ perl -0777pe 's/.*?5[^\n]*\n?//s' file
11
12
13
14
15
$ perl -0777pe 's/.*?10[^\n]*\n?//s' file1
foo
bar

説明する

  • -pe:入力ファイルを1行ずつ読み込み、与えられたスクリプトを-e各行に適用して印刷します。
  • -0777:ファイル全体をメモリに保存します。
  • s/.*?PAT[^\n]*\n?//s:最初に表示されるまで、PAT行末まですべてを削除します。

大きなファイルの場合、ファイルを2回読み取ることを避ける方法はありません。それは次のとおりです。

awk -vpat=5 '{
              if(NR==FNR){
                if($0~pat && !a){a++; next} 
                if(a){print}
              }
              else{ 
                if(!a){print}
                else{exit} 
              }
             }' file1 file1

説明する

  • awk -vpat=5:実行しawkて変数patをに設定します5
  • if(NR==FNR){}:最初のファイルの場合。
  • if($0~pat && !a){a++; next}:行が値と一致し、pat定義されてaいない場合は、a行を追加して次の行に移動します。
  • if(a){print}a定義されている場合(ファイルがパターンと一致する場合)、行を印刷します。
  • else{ }:最初のファイルではない場合(したがって2番目のパスです)。
  • if(!a){print}定義されていない場合はaファイル全体が必要なため、すべての行が印刷されます。
  • else{exit}a定義されている場合は、最初のステップですでに印刷されているため、ファイルを再処理する必要はありません。

答え2

牛に似た一種の栄養grep; cat:

{   grep -m1 'pattern' && 
    cat || ! cat ./infile
}   <./infile

POSIXsed; cat:

{ sed -ne'/PATTERN/q;H;1h;$!d;x;p'; cat; } <infile

牛に似た一種の栄養sed; cat:

{ sed -une'/PATTERN/q;H;1h;$!d;x;p'; cat; } <infile

(ただ追加してください-u


共有すればいい

上記のすべてのコマンドは、親プロセスread()(シェルのファイル記述子)のファイル記述子であるために機能します。その項目open()とその子はその記述子を継承します。ここではすべて標準入力で解決します。他のほとんどすべての種類の継承環境とは異なる傾向があるファイル記述子の1つの点は、子プロセスができる親環境のファイル記述子に影響します。

これらは定期的に、lseek()- 有能な./infile-sed(GNUのnbufferedモードを除く-u。これは、各プロセスが依然としていくつかのバッファリングを実行しますが、操作が完了するとlseek()記述子は影響を受けた最後のポイントに戻ります。そうしないと、アイテムを正しくソートするのが難しくなります。ddこの効果を得るために使用することはできますが)

同じ記述子が次にも渡されるのでNextシェルで最後の子プロセスがオフセットを変更した場合、最後の子プロセスが停止した時点から次のコマンドがすぐに入力されます。だから私たちは...

seq 10 >nums
{   grep -m1 5; cat; } <nums

grep5一致するものが1つだけ印刷されたら、最初の一致の入力を終了し、cat5以降の改行文字の後にstdinをstdoutにコピーし始めます。

5
6
7
8
9
10

もう一つのことは、grep戻り値が入力で一致するものを見つけたかどうかを簡単に知ることができることです。

{   grep -m1 pattern && 
    cat || ! cat ./infile
}   <./infile

...grep一致するものがあれば 0 を返し、&& catそうでなければ全体を||コピーします。cat./infile出力します。


いくつかのgrep


seq 100 >nums
only_after()(
    [ -f "$1" ] && {
    >/dev/null \
    grep -m1 "$2" &&
    cat  ||! cat "$1"
} <"$1")
only_after nums '[89]\{2\}'

89
90
91
92
93
94
95
96
97
98
99
100

grep戻りによって標準入力をすべて消費したかどうかが表示されます。 trueを返す確率が次の場合とても良いcatまだやるべきことが残っています。(これが起こらない唯一のケースは、grep入力の最後の行で最初の一致が見つかった場合です。この場合、規則に従って何も印刷しないでください。)。ただし、一致を検索するストリーム全体を消費し、失敗した場合はfalseを返すため、2番目のストリームはファイル全体を||cat印刷します。

このように:

seq 5 >nums
only_after nums 8; echo return: "$?"

1
2
3
4
5
return: 1

いくつかのsed

seq 200 >nums
{ sed -une'/190/q;H;1h;$!d;x;p'; cat; } <nums

191
192
193
194
195
196
197
198
199
200

...〜までの前のスペースsedに各入力行を積み重ねます。HPATTERNsed入力を見つけて完全に終了して残りの内容を残すcatか、$最後の行を見つけてsed pこの時点で保存したすべての内容を印刷します。このように:

seq 10 >nums
{ sed -une'/190/q;H;1h;$!d;x;p'; cat; } <nums

1
2
3
4
5
6
7
8
9
10

sed残念ながら、メモリの可用性と実装によっては競合が発生しやすくなります。さらに、GNUは、パフォーマンスに有意な影響を与える可能性があるnbufferedモードに切り替えないsed限り、通常他のソフトウェアとうまく機能しません。一方、POSIXはこのようにうまく機能するように指定されているので、確かに試してみる価値のある方法です。-used

lseek()- 入力可能(例:パイプ)以下も同様に機能できます。

seq 200 | sed -ne'/195/!{H;1h;$!d;x;:p' -ep -e'};n;bp'

196
197
198
199
200

...または...

seq 3 | sed -ne'/195/!{H;1h;$!d;x;:p' -ep -e'};n;bp'

1
2
3

所定の位置で編集


変えたいなら./infile- つまり所定の位置で編集- それではできます。実際にまず、一時ファイルにバッファリングして次の場所に書き込みます。

{   g=$(grep -m1  pattern) &&
    cut -c2- <<IN >./infile
$(  printf " %s\n" "$g"    &&
    paste -d\  /dev/null -  )
IN
} <./infile

...パターンが見つからない場合は何もしません。いいえ読む./infile複数回 - しかし、成功した一致のために、常に処理された尾を完全にバッファリングします。./infile書き換える前に一時ファイルに出力./infile。具体的に言えば、ここにシェル文書を作成するinfileの唯一の部分は次のとおりです。後ろに grepマッチ。grep試合で消費されるすべての入力消費し続けるしたがって、バッファの終わりだけが一時バッファに格納されます。

さらに、ほとんどのシェルはここでドキュメントをサポートしています。通常、/tmpLinuxシステムの/tmp場合と同様に、tmpfsこれはそのシステムにバッファリングされた部分がディスクにまったく表示されないことを意味します。しかし、公平に言えば、カーネルがファイルキャッシュなどを処理する方法のためにキャッシュするメモリが十分であると仮定して、tmpfsへの書き込みと他の場所への書き込みの間に大きな違いはありません。/tmpたぶん、もっと明確に話すことができます。シェル度unlink()バイトが書き込まれる前のバッファファイルです。したがって、読み取りおよび/または書き込み記述子が開いている間にのみ存在します。掃除するものはありません。


全部包んで


これを行うために小さなプログラムを書いた...

allor()(
        set -f; unset o z i m;  OPTIND=1 IFS="
"
        op()    while   getopts :i:o:m: O               &&
                        case    $O$OPTARG               in
                        ([$z]*|m*[!0-9]*|[!imo]*) ! :   ;;
                        (o+)    o= O=;; (o-)     O=     ;;
                        esac||! o=${o+${o:-$i}}  m=${m:-1}
                do      eval "z=$z${O:-o #} $O=\$OPTARG"||exit
                done

        op "$@";[ -f "${i:?No input specified!}" ]      ||i=
        exec < "${i:?Input is not a regular file!}"     &&
        shift   $((OPTIND-(${#O}+1)))                   &&
        z=$( !  { {     grep -m$m "$@" 2>&3 |
                   >&4  sed  -ne'$=;$s/^/ /p'
                } 3>&1| grep . >&2;}   4>&1 )           &&
        set     ${z:?No match found!}   ${o:+'>"$o"'}   &&
        case    $((m==$1))$o    in      (0"$i") ! :     ;;
        (0*)    <"$i"   eval "  cat $3     &&   ! :"    ;;
        (1*)    <<-i    eval "  cut -c2-   $3"
                $(      printf %s\\n $2;paste /dev/null -)
                i
        esac
)

...いくつかのオプションの解析などが追加されます。デフォルトでは、必要grepなパラメータを渡すことができます。すべてのパラメータはそのまま渡されます。とは別に-iまたは-o-m

-i対応するパラメータを使用して入力ファイルを指定できます。-o-標準出力への書き込み(とにかくデフォルトの動作)を使用したり、-o+ファイルを所定の位置に編集したり、書き込み可能なパス名を編集したりできます。一致回数を指定できます。つまり、それ以降のすべてのファイルをインポートできます。-i-o-m-m一致を計算します。入力で一致が見つからない場合は、ファイル全体のみを検索してください。最初の-[iom]項目が有効なスイッチではないか、2番目の項目-[io]がに直接渡されるすべての引数ですgrep

要求された一致が成功したかどうか、および書き込みを試みる前に出力を配置する必要がある場所をテストします。たとえば、一致が失敗し、出力が再度次に渡される場合./infile何もしないで去るよ./infile一人で。一致が成功し、outfileとinfileが等しい場合、infileは短縮されます。ただし、一致が失敗し、出力が別の場所に向かう場合にのみcat出力に入力されます。

小さなデモ:

seq 20 >nums
allor -inums -m2 5

15
16
17
18
19
20

...そして...

seq 10 >nums
allor -inums -m2 5; echo return: "$?"

1
2
3
4
5
6
7
8
9
10
return: 1

...そして...

seq 20000 >nums
allor -m1999 -inums -o+ 5$; cat nums

19985
19986
19987
19988
19989
19990
19991
19992
19993
19994
19995
19996
19997
19998
19999
20000

答え3

GNU sedを使用すると、次のことができます。

:x;/PATTERN/{s/.*//;:z;N;bz};N;bx

7たとえば、一致させるパターンとしてを使用してから生成されたデータを入力するseqと、数字8から20(17を含む)が印刷されます。

seq 20 | sed ':x;/7/{s/.*//;:z;N;bz};N;bx'

これは1から6まで印刷されます。

seq 6 | sed ':x;/7/{s/.*//;:z;N;bz};N;bx'

コメントで指摘したように、これはファイル全体をメモリに効果的に読み込むことです。あなたの場合、これは可能なことです。

また、現在のケース8〜20で追加の先行改行が出力されるという警告があります。これを強力に除去する方法を理解しようとしています。これがあなたのアプリケーションにとって重要であるかどうかはわかりません。

答え4

TxR回避策はコマンドラインで次のとおりです。

$txr -c'@(おそらく)
@(飛び越える)
@(トレーラー)
@/柄/
@(終了)
@(繰り返し)
@ワイヤー
@(do(リリースライン))
@(終了) -

pattern例: 表示されない行。コード略語:

$txr -c'@(おそらく)
[...] '-
第二
D
Ctrl-DEnter
第二
D

pattern発生時:

$txr -c'@(おそらく)
[...]
@(終了) -
第二
模様
模様
X
X
ワイ
ワイ
Ctrl-DEnter

ご覧のとおり、patternそのようなことが起こると線がエコーされ始めます。

ロジックは非常に簡単です。含まれる材料は@(maybe)...@(end)オプションで一致します。@(skip)複数行をスキップする行があり、その後に@(trailer)「次を末尾のコンテキストに一致させる(使用せず)」という意味の行があります。 (この機能とその名前はLexの末尾のスラッシュコンテキストに触発されました。)削除すると、パターンに一致する行が出力から除外されます@(trailer)

もちろん@/pattern/正規表現です。暗黙的に固定されているため、行全体と一致する必要があります。したがって、次を含む行を一致させるには、またはをabc使用します。@/.*abc.*/@(skip)abc@(skip)

このパターンが発生しない場合、入力全体がskip内部的にスキャンされ、maybe最終的に失敗します。maybeこの失敗を捉え、隠して成功を達成しましょう。その後のデータは、maybe内部的に失敗した元の入力(つまりストリームの開始)とmaybe一致します。

@(repeat)最後に、出力副作用を含む反復一致構造があります。

@(data ...)コマンドを使用して変数から現在のデータカーソル(遅延リストポインタ)をキャプチャし、startEOFで再キャプチャし、古代のLisp関数を使用してldiff出力を計算する他のTXRソリューション:

$ txr -c '@(maybe)
@(skip)
@/pattern/
@(end)
@(data start)
@(skip)
@(eof)
@(data end)
@(do (tprint (ldiff start end)))' -

オプションで、一部の行とパターンを一致させた後、キャプチャが開始されます。パターンが表示されない場合は、@(maybe)...@(end)ブロックが存在しないかのようにデータの先頭がキャプチャされます。

つまり、「パターンで終わるいくつかの行をスキップすることもできます。どちらの場合も、位置を開始としてマークし、EOFにジャンプして位置を終了としてマークします。開始と終了の間のすべての内容を印刷します」

関連情報