shell 查找并返回包含字符串的行块

1sbrub3j  于 12个月前  发布在  Shell
关注(0)|答案(4)|浏览(138)

我有一个下面类型的大文件:

key = asbh
some
lines
of
**text**

key = kafeia
some
more
**text**
and
additionally
more
**text**
and
more

key = lklfh
this
is
another
block

字符串
注意(如果重要的话):'key'的行永远不包含感兴趣的字符串('text')。
我把一个块中从“key”开始的一行到下一行之间的所有行称为块(在这个例子中,是3个块)。我想返回所有包含字符串“text”的块。即所需的输出:

key = asbh
some
lines
of
**text**

key = kafeia
some
more
**text**
and
additionally
more
**text**
and
more


我尝试了多种方法,我希望我的方向是正确的,但似乎不能让它工作。这些是我的尝试:
1.第一个月
我相信这可能是从它第一次看到“key”开始,然后继续前进(因此返回了很多不相关的块),直到它在某个地方看到“text”并停止。这是受到一个类似的问题here的启发,但它没有拉取多个块的条件,也没有匹配块内的模式。

  1. less myfile.txt | grep -Pzl '(?s)^key([^key]|\n)*text' | less
    我认为这可能会更好,如果我能让它工作,我可能会扩展它,因为它目前只尝试获得键和文本之间的文本(直到下一个键)。
    1.我试图理解if语句是如何工作的,特别是考虑到这个线程,但我是unix的新手,所以如果有人能解释一下,我会非常感激。
mv1qrgav

mv1qrgav1#

这可能对你有用(GNU sed):

sed -n '/^key/!{H;$!d};x;/text/p' file

字符串
关闭隐式打印-n
如果一行不是开始key,将其追加到保留空间并删除,除非它是最后一行。
否则,交换到保持空间,如果集合匹配text,则打印它。
注意:文件结束条件自然会下降到匹配条件。当key匹配时,保持/模式空间会发生触发。

oyxsuwqo

oyxsuwqo2#

awk的“段落”模式很简单,因为你有一个空白行分隔块。

$: awk -v RS= -v ORS='\n\n' /text/ file
key = asbh
some
lines
of
**text**

key = kafeia
some
more
**text**
and
additionally
more
**text**
and
more

字符串
我添加了显式的换行符;注意,这将在文件末尾追加一个,即使它不存在。
说明:
-v在运行中设置v变量。
RS=R ecord S分离器设置为空,启用“段落模式”,读取直到看到一个 * 空白 * 行,并将其读取的所有内容(是的,整个块)称为一个“记录”。
ORS=$'\n\n'O输出R ecord S分隔符设置为 * 两个 * 换行符,在段落模式读取删除它之后添加空白行 back
/text/扫描几行的“记录”,寻找触发器字符串。如果返回true,因为我没有提供任何其他代码来执行,它将打印该块。(注意,由于这是一个简单的检查,我不需要在它周围加上引号;我选择关闭它们以减少噪音。YMMV。)

附录

只是注意到tshiono的评论几个小时前我。
我自己也做了相应的修正-

1dkrff03

1dkrff033#

为了代码的整洁性和可读性,我建议使用Python或bash脚本文件。

with open('file.txt', 'r') as file:
    lines = file.readlines()

blocks = []
current_block = []

for line in lines:
    if line.startswith('key'):
        # Check if the current block contains 'text'
        if any('text' in block_line for block_line in current_block):
            blocks.extend(current_block)
        current_block = [line]
    else:
        current_block.append(line)

# Check the last block in case it ends with 'text'
if any('text' in block_line for block_line in current_block):
    blocks.extend(current_block)

# Print or use the blocks as needed
for block in blocks:
    print(block.strip())

字符串
如果你坚持在一行中完成这个操作,你也可以使用下面的代码:

awk '/^key/ { if (block ~ /text/) print block; block=""; } { block = block $0 RS } END { if (block ~ /text/) print block }' file.txt


我在几个文件上检查了这个方法,它是正确的。我希望它对你有用。

ncecgwcz

ncecgwcz4#

Bash可能不是最好的工具,但它肯定可以完成这项工作。下面的代码片段没有任何错误处理;它只是一个碰巧可以处理问题输入的例子。

read_blocks() {
  local -r needle="$1"
  local -n _blocks="$2"  # declare -A
  local -n _keys="$3"    # declare -a
  local -i needle_seen=0
  local line key='UNSET'
  _blocks=()
  _keys=()
  while IFS= read -r line; do
    if [[ "$line" = 'key = '* ]]; then
      ((needle_seen)) && _keys+=("$key") || unset '_blocks["$key"]'
      ((needle_seen = 0)) || :
      key="${line#key = }"
    else
      [[ "$line" = *"$needle"* ]] && ((++needle_seen)) || :
      _blocks["$key"]+="$line"$'\n'
    fi
  done
  ((needle_seen)) && _keys+=("$key") || unset '_blocks["$key"]'
}

declare -A blocks
declare -a keys
read_blocks 'text' 'blocks' 'keys' < /path/to/input

for key in "${keys[@]}"; do
  printf 'key = %s\n' "$key"
  printf '%s' "${blocks["$key"]}"
done

字符串
如果块的顺序不需要保留,那么您可以完全丢弃keys数组,并在写入输出时简单地忽略${!blocks[@]}

相关问题