如何保留文件/命令的前n行,但其余部分grep?

时间:2020-05-21 23:18:21

标签: awk sed filtering

最容易举一个例子。

bash-$ psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;


        relname         | reltype
------------------------+---------
 bme_reltag_02          |       0
 bme_reltag_type1_type2 |       0
 bme_reltag_10          |       0
 bme_reltag_11          |       0
 bme_reltag_cvalue3     |       0  ? what I care about

但是我真正感兴趣的是其中包含cvalue的任何内容。无需手动修改每个查询(是的,我知道我可以做到),我可以egrep关心自己的事情。

psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;' | egrep 'cvalue'

但这会删除带有列标题的前两行。

 bme_reltag_cvalue3     |       0

我知道我也可以这样做:

psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;' | head -2 && psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;' | egrep 'cvalue'

        relname         | reltype
------------------------+---------
 bme_reltag_cvalue3     |       0

但是我真正想做的是以某种方式保留某些行的头部(或尾部),然后以另一种方式处理其余的行。

我在这里的特殊用例是重复任意psql选择的内容,但是我很好奇该域中的bash功能。

我之前通过写入临时文件然后分多个步骤处理临时文件来完成此操作,但这不是我想要的。

4 个答案:

答案 0 :(得分:3)

一个while读取循环和grep,如果可以的话。

#!/usr/bin/env bash

while IFS= read -r lines; do
  [[ $lines == [12]* ]] && echo "${lines#*:}"
  [[ $lines == *cvalue[0-9]* ]] && echo "${lines#*:}"
done < <(psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;' | grep -n .)

如果没有grep,则可以选择一个计数器来知道行号,这将是纯bash解决方案。

#!/usr/bin/env bash

counter=1
while IFS= read -r lines; do
  [[ $counter == [12] ]] && echo "$lines"
  [[ $lines == *cvalue[0-9]* ]] && echo "$lines"
  ((counter++))
done < <(psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;')

如果bash4+可用。

#!/usr/bin/env bash

mapfile -t files < <(psql -c 'select relname, reltype from pg_catalog.pg_class limit 5;')

printf '%s\n' "${files[0]}" "${files[1]}"

unset 'files[0]'  'files[1]'

for file in "${files[@]}"; do
  [[ $file == *cvalue[0-9]* ]] && echo "$file"
done

  • 默认情况下,内置read会去除开头和结尾的空格,因此在这种情况下我们不希望这样做,因此我们使用IFS=

  • grep -n .在行号上添加:

  • [12]是一个非正则表达式的glob,表示1或2,并且*的glob如果是该行的第一个字符,则将匹配。

  • *cvalue[0-9]*将与cvalue及其旁边的任意整数/数字匹配。

  • "${lines#*:}"是参数扩展,剥离了前导:

  • <( )被称为进程替换。

答案 1 :(得分:3)

$ psql -c ... | awk 'NR<3 || /cvalue/' file

答案 2 :(得分:2)

sed可以使用其范围功能仅在第3行及以后的行上进行操作

sed '3,${/cvalue/!{d;};}'

概念证明

$ cat ./psql
relname                | reltype
------------------------+---------
bme_reltag_02          |       0
bme_reltag_type1_type2 |       0
bme_reltag_10          |       0
bme_reltag_11          |       0
bme_reltag_cvalue3     |       0

$ sed '3,${/cvalue/!{d;};}' ./psql
relname                | reltype
------------------------+---------
bme_reltag_cvalue3     |       0

说明

  • 3,${...;}:从第3行开始处理,直到文件$结束
  • /cvalue/!{d;}:删除d与(!)正则表达式/cvalue/不符的任何行

答案 3 :(得分:0)

您可以使用bash .. tail。和head命令

cat file.sql | head -n 15 > head.sql

用行数替换15

或将头替换为尾...用于文件的底部

相关问题