Как я могу получить строки, где определенное слово повторяется ровно в N раз?

case case

Это самое портативное решение, будет работать даже на старых оболочках Bourne и оболочке Korn

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Пример прогона:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Обратите внимание, что вам не нужно специально использовать echo, вы можете использовать exit 1 и exit 0 для обозначения успеха или сбоя.

То, что мы могли бы сделать, также - создать функцию (который может быть использован в больших сценариях, если необходимо) с конкретными значениями возврата (0 по совпадению, 1 не соответствует):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

is_substring(){
    case "$2" in
        *$1*) return 0;;
        *) return 1;;
    esac
}

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

Этот конкретный подход полезен для операторов if-else в bash. Также в основном переносится

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Python

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Ruby

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring
1
задан 5 January 2015 в 06:34

6 ответов

Предполагая, что ваш исходный файл - tmp.txt,

grep -iv '.*this.*this.*this.*this' tmp.txt | grep -i '.*this.*this.*this.*'

Левый grep выводит все строки, у которых нет 4 или более нечувствительных к регистру вхождений «this» в tmp.txt.

Результат передается вправо grep, который выводит все строки с 3 или более вхождениями в результат левого grep.

Обновление: благодаря @Muru, вот лучшая версия этого решения,

grep -Eiv '(.*this){4,}' tmp.txt | grep -Ei '(.*this){3}'

заменить 4 на n + 1 и 3 на n.

9
ответ дан 24 May 2018 в 00:27
  • 1
    Это не удастся для N & gt; 4. И первый grep должен заканчиваться на *. – prakharsingh95 4 January 2015 в 23:02
  • 2
    Я имею в виду, что вы не можете записать это для N = 50. И вопрос будет ровно три, так что вам нужен еще один grep, который отбрасывает все выходы, содержащие меньше или равные двум this. [F2] – prakharsingh95 4 January 2015 в 23:09
  • 3
    @ prakharsingh95 Это не сработало для n & gt; 4 и * не требуется в первом grep. – Sri 4 January 2015 в 23:13
  • 4
    @KasiyA, что вы думаете о моем ответе? – Sri 4 January 2015 в 23:20
  • 5
    Упростите это немного: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}' - это может сделать его практичным для N = 50. – muru 5 January 2015 в 00:20

В python это выполнит задание:

#!/usr/bin/env python3

s = """How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this"""

for line in s.splitlines():
    if line.lower().count("this") == 3:
        print(line)

выдает:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Или читать из файла с файлом в качестве аргумента:

#!/usr/bin/env python3
import sys

file = sys.argv[1]

with open(file) as src:
    lines = [line.strip() for line in src.readlines()]

for line in lines:
    if line.lower().count("this") == 3:
        print(line)
Вставьте скрипт в пустой файл, сохраните его как find_3.py, запустите его командой:
python3 /path/to/find_3.py <file_withlines>

Конечно, слово «это» можно заменить на любое другое слово (или другая строка или строка), а число вхождений в строке может быть установлено на любое другое значение в строке:

    if line.lower().count("this") == 3:

Edit

Если файл был бы большим (сотни тысяч / миллионы строк), код ниже был бы быстрее; он считывает файл на строку, а не сразу загружает файл:

#!/usr/bin/env python3
import sys
file = sys.argv[1]

with open(file) as src:
    for line in src:
        if line.lower().count("this") == 3:
            print(line.strip())
9
ответ дан 24 May 2018 в 00:27
  • 1
    Я не эксперт по python, как я могу читать из файла? благодаря – αғsнιη 4 January 2015 в 21:58
  • 2
    @KasiyA отредактирован для использования файла в качестве аргумента. – Jacob Vlijm 4 January 2015 в 22:06
  • 3
    Просто любопытно: почему вы не использовали генератор во втором фрагменте кода? – muru 6 January 2015 в 04:16

Вы можете сыграть бит с awk для этого:

awk -F"this" 'BEGIN{IGNORECASE=1} NF==4' file

Это возвращает:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Объяснение

Что мы делаем для определения разделителя полей на this. Таким образом, линия будет иметь столько полей +1, сколько раз появляется слово this. Чтобы сделать регистр нечувствительным, мы используем IGNORECASE = 1. См. Ссылку: Чувствительность к регистру в соответствии. Тогда просто нужно сказать NF==4, чтобы все эти строки имели this ровно три раза. Больше не нужно кода, так как {print $0} (т. Е. Печатать текущую строку) является поведением по умолчанию awk, когда выражение оценивается как True.
6
ответ дан 24 May 2018 в 00:27

Предполагая, что строки хранятся в файле с именем FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
5
ответ дан 24 May 2018 в 00:27
  • 1
    Спасибо, вы можете удалить команду sed ... и добавить -o вариант для grep -oi .... – αғsнιη 4 January 2015 в 22:25
  • 2
    Упрощение: $(grep -ic "this" <<<"$line") – muru 4 January 2015 в 22:43
  • 3
    @muru Нет, опция -c будет подсчитывать количество строк , которые совпадают с "этим" не число "этого" слова в каждой строке. – αғsнιη 4 January 2015 в 22:50
  • 4
    @ KasiyA Ах, да. Виноват. – muru 4 January 2015 в 22:51
  • 5
    @KasiyA, не будет -l и -w эквивалентным в этом случае? – prakharsingh95 4 January 2015 в 23:00

Если вы находитесь в Vim:

g/./if len(split(getline('.'), 'this\c', 1)) == 4 | print | endif

Это будет просто печатать согласованные строки.

4
ответ дан 24 May 2018 в 00:27
  • 1
    Хороший пример поиска строк с n вхождениями слова при использовании Vim. – Sri 5 January 2015 в 10:05

Однострочное решение Ruby:

$ ruby -ne 'print $_ if $_.chomp.downcase.scan(/this/).count == 3' < input.txt                                    
How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Работает довольно просто: мы перенаправляем файл на stdin ruby, ruby ​​получает строку от stdin, очищает его с помощью chomp и downcase , и scan().count дает нам количество вхождений подстроки.

0
ответ дан 24 May 2018 в 00:27

Другие вопросы по тегам:

Похожие вопросы: