假设我有一个文件包含:
⟫ 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 已经删除了所有上下文行。-B
grep -v
⟫ grep created_at schema.rb | grep -v precision: -B3
t.datetime "created_at"
但将其添加到第一个grep
还为时过早,因为grep -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
也许是一个简单的脚本——如果它需要的不仅仅是一个简单的 sed
脚本,我也可以将其写入,ruby
以便更容易阅读和维护)。
答案1
如果我将 2 个命令链接在一起,我认为我尝试做的事情是不可能的grep
(因为上下文行与每个单独的grep
命令相关)。
我突然想到,消极的前瞻可能正是我想要的。然后这一切都可以通过一个grep
命令来完成。
令我惊讶的是,看起来 GNUgrep
实际上确实支持正则表达式后向/前向 — 但前提是您使用该--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解决方案
$ 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”中。查找created_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
难道简单地反转两个grep
s 就可以做你想要的事情,消除precision
前面的行,这样它们就不会与created_at
第二个中的行匹配吗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