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

问题描述 投票:0回答:2

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

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. less myfile.txt | sed -n '/key/,/text/p' | less

    我相信这可能从它第一次看到“键”开始,然后继续前进(因此返回很多不相关的块),直到它在某处看到“文本”并停止。 这是受到类似问题here的启发,但没有拉动多个块的条件,也没有匹配块内模式的条件。

  2. less myfile.txt | grep -Pzl '(?s)^key([^key]|\n)*text' | less

    我认为这可能会更好,如果我能让它工作,我可能会扩展它,因为它当前尝试仅获取键和文本之间的文本(直到下一个键)。

  3. 我尝试理解if语句是如何工作的,特别是考虑到这个线程,但我是unix新手,所以如果有人可以解释,我将非常感激。

bash shell sed grep
2个回答
0
投票

为了代码的简洁性和可读性,我建议使用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

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


0
投票

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[@]}
    

© www.soinside.com 2019 - 2024. All rights reserved.