その行で一致を検索し、一致しないが一致する行を含むコンテキスト行を検索するにはどうすればよいですか?

その行で一致を検索し、一致しないが一致する行を含むコンテキスト行を検索するにはどうすればよいですか?

以下を含むファイルがあるとしましょう。

⟫ cat schema.rb 
  create_table "things", id: :serial, force: :cascade do |t|
    t.string "other_column"
    # ...
    t.datetime "created_at"
  end

  create_table "users", id: :serial, force: :cascade do |t|
    t.citext "email"
    # ...
    t.datetime "created_at", precision: 0
  end

created_at一致するすべての行を探したいのですがいいえマッチprecision:。それは簡単です:

⟫ grep created_at schema.rb 
    t.datetime "created_at"
    t.datetime "created_at", precision: 0

⟫ grep created_at schema.rb | grep -v precision:
    t.datetime "created_at"

しかし、一致する行の一部のコンテキスト行を取得し、その行がどのブロックに表示されるかを確認するにはcreate_tableどうすればよいですか?-C最初のgrepがすでにすべてのコンテキスト行を削除しているため、最後に/フラグを追加するのは遅すぎます。-Bgrep -v

⟫ grep created_at schema.rb | grep -v precision: -B3
    t.datetime "created_at"

しかし、最初の行に追加するのは時期尚早ですgrepgrep -v一致する行だけが削除され、一致する行を囲むコンテキスト行は削除されないためです。

⟫ grep created_at -B3 schema.rb | grep -v precision: -B3
  create_table "things", id: :serial, force: :cascade do |t|
    t.string "other_column"
    # ...
    t.datetime "created_at"
--
  create_table "users", id: :serial, force: :cascade do |t|
    t.citext "email"
    # ...

最初の行に一致する行のコンテキスト行のみを含める方法はありますかgrep(または同等にgrep -v一致する行の周囲のコンテキスト行を削除します)?

  create_table "things", id: :serial, force: :cascade do |t|
    t.string "other_column"
    # ...
    t.datetime "created_at"

それともこれを行うための他のコマンドはありますか?

sedたぶん簡単なスクリプトかもしれません。シンプル sedrubyスクリプトを読みやすく保守しやすくするために作成することもできます。

答え1

2つのコマンドを一緒にリンクすると、私がやろうとしていることは可能ではないと思いますgrep(コンテキスト行が各個々のgrepコマンドに関連付けられているため)。

否定的な見通しは、私が望むものかもしれないと思いました。これにより、これらすべてをgrep単一のコマンドで実行できます。

驚くべきことに、GNUはgrep実際に正規表現のバック/フォワードをサポートしているようです。ただし、そのオプションを使用している場合にのみ可能です--perl-regex

grep私が探していることを知らせるコマンドは次のとおりです。

⟫ grep --perl-regexp 'created_at(?!(.*precision:))' schema.rb -B3
  create_table "things", id: :serial, force: :cascade do |t|
    t.string "other_column"
    # ...
    t.datetime "created_at"

答え2

奇妙なソリューション

$ awk '/create_table/,/created_at/&&!/precision:/' schema.rb
  create_table "things", id: :serial, force: :cascade do |t|
    t.string "other_column"
    # ...
    t.datetime "created_at"
  create_table "users", id: :serial, force: :cascade do |t|
    t.citext "email"
    # ...
    t.datetime "created_at", precision: 0
  end
$

次のいずれも実装されていません。正確に何が欲しいですか?しかし、どんな面でも役に立つ場合を備えておきます。

create_table 行に一致する sed ソリューションを印刷します。

create_table 行を見つけて見つかった場合は、保存バッファ "h" に保存します。 Create_at の後に精度を探し、見つかった場合は無視します。 Created_atのみを参照して見つかった場合は、印刷してください。

$ sed -n '/create_table/h;/created_at.*precision:/d;/created_at/{H;g;p}' schema.rb
  create_table "things", id: :serial, force: :cascade do |t|
    t.datetime "created_at"
$

create_table 行に一致する awk ソリューションを印刷します。

一致する create_table 行を具体的にターゲットにするには、次のようにします。

$ awk '/create_table/{t=$0}/created_at/&&!/precision:/{print t"\n"$0}' schema.rb
  create_table "things", id: :serial, force: :cascade do |t|
    t.datetime "created_at"
$

テーブル印刷のためのAwkソリューション

さらにステップが進み、一致する行を持つテーブル名のみを希望する場合は、次を使用できます。

$ awk '/create_table/{t=$2}/created_at/&&!/precision:/{print substr(t,2,length(t)-3)}' schema.rb
things
$

答え3

単に2つのgrepsを逆にすると、必要に応じて動作せず、precision前の行を削除してcreated_at2番目の行と一致しませんかgrep

grep -v precision schema.rb | grep -A1 -B3 created_at 
create_table "things", id: :serial, force: :cascade do |t|
   t.string "other_column"
   # ...
   t.datetime "created_at"
 end

関連情報