【问题标题】:grep two files (a.txt, b.txt) - how many lines in b.txt starts (or ends) with the words from a.txt - output: 2 files with the resultsgrep 两个文件 (a.txt, b.txt) - b.txt 中有多少行以 a.txt 中的单词开始(或结束) - 输出:2 个文件和结果
【发布时间】:2014-04-20 16:47:47
【问题描述】:

我知道我问的太多了,但也许你也可以帮助解决这个问题。

a.txt 包含单词,b.txt 包含字符串。

我想知道 b.txt 中有多少字符串以 a.txt 中的单词结尾

示例: 一个.txt

apple
peach
potato

b.txt

greenapple
bigapple
rottenapple
pinkpeach
xxlpotatoxxx

输出

3 apple greenapple bigapple rottenapple
1 peach pinkpeach

我想有一个 grep 的解决方案,因为它比 awk 快得多。

你们能帮帮我吗?

【问题讨论】:

  • 这是个好问题..没问题..不要害怕问
  • 标题说“开始于”,但你说“结束于”。这是正确的吗?
  • 已修改。我想为他们两个提供解决方案。一个带有结果的输出文件:如果它以开头,一个带有结果:如果它以...结尾。
  • 当我从 Michael 那里运行time bash_script 时,我确实得到了real 0m0.132stime awk ... 我得到了real 0m0.003s。那么什么更快呢?
  • 您希望输出如何排序?出现次数? a.txt中的单词按字母顺序排列?

标签: string bash grep comparison


【解决方案1】:

这是awk 解决方案

awk 'FNR==NR{a[$1]++;next} {for (i in a) {if ($0~i"$") {b[i]++;w[i]=w[i]?w[i] FS $0:$0}}} END {for (j in b) print b[j],j,w[j]}' a.txt b.txt
3 apple greenapple bigapple rottenapple
1 peach pinkpeach

使用grep 做到这一点并不简单或根本不可能

它是如何工作的(没那么复杂)?

awk '
FNR==NR{                        # Run this part for first file (a.txt) only
  a[$1]++                       # Store it in an array a
  next}                         # Skip to next record
  {                             # Run this part for file b.txt
  for (i in a) {                # Loop trough all data in array a
    if ($0~i"$") {              # Does b.txt have some from array a at the end of it?
      b[i]++                    # Yes , count it
      w[i]=w[i]?w[i] FS $0:$0   # and store the record it found it in in array w
      }
    }
  } 
END {                           # When both file has been read do the END part
  for (j in b)                  # Loop trough all element in array b and
    print b[j],j,w[j]}          # Print array b, index and array w
  ' a.txt b.txt                 # Read the two files

【讨论】:

  • 您好,感谢您的帮助。但请记住,'potato' 不是热门,因为 'xxlpotatoxxx' 不是以 'potato' 结尾的。它包含但不结束。
  • -如果我想在 txt 文件中获取结果,我需要更改哪些内容? (或在两个文件中:一个文件用于“如果以开头”和“如果以结尾”)
  • 我已经修复了potato。要存储在文件中,只需执行awk 'code' a.txt b.txt > result.txt
  • 它已启动并正在运行。如果你能帮我做同样的事情,但“如果它开始于”,那就太好了。
  • @Jotne 你可以通过 if ($0 ~ i "$") 删除 f=i"$" 部分
【解决方案2】:

这是一个仅依赖于bashgrep 的解决方案。恕我直言,它比 awk-only 方法更容易理解:

#!/bin/bash

# Set input parameters (usually a good idea than hardcoding them)
WORDFILE=a.txt
SEARCHFILE=b.txt

# Read 'a.txt' word by word (i.e. line by line)
while read word; do
  # Get numbers of hits
  num=`grep "$word\$" $SEARCHFILE | wc -l`

  # If no line matches in 'b.txt', skip this word
  if [ $num -eq 0 ]; then
    continue
  fi

  # Print number of hits and search word
  printf "%d $word" $num

  # Print all lines that match from file 'b.txt'
  for found in `grep "$word\$" $SEARCHFILE`; do
    printf " $found"
  done

  # Print newline
  printf "\n"
done < $WORDFILE

编辑

如果您想将结果存储在一个文件中,您可以按照通常的方式重定向上述脚本的输出,例如

./find_matching_ends.sh > matching_ends.txt

如果要搜索以单词开始的行,则需要将grep 模式从"$word\$" 更改为“^$word”。如果您希望此搜索与匹配端的搜索同时发生,您需要在脚本中移动上面的重定向,例如

...
printf "%d $word" $num > matching_ends.txt
...

当您搜索匹配的末端时,并且

...
printf "%d $word" $num > matching_starts.txt
...

当您正在寻找以搜索词开头的行时。

【讨论】:

  • -如果我想在 txt 文件中获取结果,我需要更改哪些内容? (或在两个文件中:一个文件用于“如果以开头”和“如果以结尾”)
  • 我的,当然 :P 是的,我的解决方案是一种 hack,但我认为它更容易理解。如果 OP真的关心速度,他应该考虑离开 Bash 领域并转向另一种语言。虽然awk 似乎也令人印象深刻。
  • 我不会说这个bash script 比awk 更容易阅读,而且它也更慢。 OP 要求速度
  • 正确,我想解析大约 100M 行而不是 50K。
  • 我发现了这个:假设我想搜索整个单词,而不是单词的一部分? grep -w 'hello' * 仅搜索作为整个单词的“hello”实例;它不匹配“奥赛罗”。如需更多控制,请使用“\”来匹配单词的开头和结尾。例如: grep 'hello\>' * 仅搜索以“hello”结尾的单词,因此它匹配单词“Othello”。我找到了这个,所以可以用 grep 来完成,但我不知道如何修改它“-.- 你的代码是一个解决方案,但仍然很慢(100M vs 50K)。
【解决方案3】:

我想提出一个基于Bash 的解决方案,避免grep。相反,它使用for-loops 和数组:

#!/usr/bin/env bash

# Set mode: start | end
mode="end"

# Read contents of input files into arrays - line by line
IFS=$'\n' read -d -r -a patterns < "$1"
IFS=$'\n' read -d -r -a targets < "$2"

# Bash 4 can use readarray
#readarray -t patterns < "$1"
#readarray -t targets < "$2"

# Alternatively use cat to get the contents into arrays (slower)
#patterns=($(cat $1))
#targets=($(cat $2))


# Iterate over both arrays to compare the strings with each other
for pattern in "${patterns[@]}"; do

    # Setup a variable that counts the hits for each pattern
    hits_counter=0

    # Setup a variable that takes the matched strings for each pattern
    hits_match=""

    # Setup a regex pattern according to the user defined mode
    if [[ "$mode" == "start" ]]; then
        regex="^${pattern}"
    elif [[ "$mode" == "end" ]]; then
        regex="${pattern}$"
    fi

    for target in "${targets[@]}"; do

        # Use regex pattern matching
        if [[ "$target" =~ $regex ]]; then

            # If we detect a match increase the counter by 1
            (( hits_counter++ ))

            # If we detect a match write it to our hits_match variable and append a space
            hits_match+="${target} "
        fi
    done

    # Print a result for each pattern if we have at least one match
    if (( hits_counter > 0 )); then
        printf "%i %s %s\n" "$hits_counter" "$pattern" "$hits_match"
    fi
done

这给出了以下结果:

./filter a.txt b.txt
3 apple greenapple bigapple rottenapple
1 peach pinkpeach

【讨论】:

    猜你喜欢
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    相关资源
    最近更新 更多