Como procurar uma correspondência e, em seguida, uma não correspondência nessa linha, mas incluir linhas de contexto para a linha correspondente?

0

Digamos que eu tenha um arquivo contendo:

⟫ 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

e quero encontrar todas as linhas correspondentes a created_at , mas não correspondendo a precision: . Isso é fácil:

⟫ 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"

mas e se eu quiser obter algumas linhas de contexto para as linhas correspondentes para que eu possa ver em qual create_table bloco elas ocorreram? Adicionar um -C / -B ao final grep -v é muito tarde porque o primeiro grep já descartou todas as linhas de contexto.

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

Mas adicioná-lo ao primeiro grep é muito cedo porque o grep -v apenas remove a linha correspondente, não as linhas de contexto em torno das linhas correspondentes:

⟫ 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"
    # ...

Existe alguma maneira de fazer com que ele inclua apenas as linhas de contexto para as linhas correspondentes do primeiro grep (ou equivalentemente, obtenha o grep -v para remover as linhas de contexto em torno de suas linhas correspondentes)?

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

Ou há outro comando que faria isso por mim?

(um simples script sed talvez - se precisar de algo mais do que um simples script sed >%, eu também posso escrevê-lo em ruby para que seja mais fácil de ler e manter ).

    
por Tyler Rick 15.08.2018 / 19:11

3 respostas

1

Eu não acho que o que eu estava tentando fazer é possível se eu encadear os comandos 2 grep (já que as linhas de contexto são relativas a cada comando individual grep ).

Ocorreu-me que um lookahead negativo pode ser o que eu quero. Então tudo pode ser feito com um único comando grep .

Para minha surpresa, parece que o GNU grep realmente tem suporte para regex lookbehind / lookahead - mas apenas se você usar a opção --perl-regex .

Aqui está um comando grep que me fornece o que eu estava procurando:

⟫ 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"
    
por 15.08.2018 / 19:37
0

solução awk

$ 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
$

Nenhum destes abaixo consegue exatamente o que você procura, mas eu vou colocá-los, caso seja útil de alguma forma.

solução sed que imprime a linha create_table e a correspondência

Procure a linha create_table, se encontrada, armazene no buffer de retenção 'h'. Procure por created_at seguido por precision, se encontrado, ignore-o. Procure por created_at sozinho, se encontrado, imprima-o.

$ 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"
$

solução awk que imprime a linha create_table e a correspondência

Se você quisesse especificamente a linha create_table contra a partida, poderia usar

$ 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"
$

solução awk que imprime a tabela

Levando um pouco mais longe, se você quisesse apenas os nomes das tabelas que tinham uma linha correspondente, você poderia usar

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

Não basta simplesmente inverter os dois grep s para fazer o que você deseja, eliminando as linhas precision antecipadamente para que elas não correspondam às linhas created_at no segundo 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
    
por 16.08.2018 / 13:01