Как найти все шаблоны между двумя символами?

Я пытаюсь найти все шаблоны между парой двойных кавычек. Позвольте говорят, что у меня есть файл с содержанием, похожи как следующее:

first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".

Я хочу ниже слов, как произведено:

One
Two
Three
Four

Поскольку Вы видите, что все строки в выводе между парой кавычек.

То, что я попробовал, является этой командой:

grep -Po ' "\K[^"]*' file

Выше команды хорошо работает, если у меня есть пространство перед первой парой " метки. Например, это работает, если мой входной файл содержит следующее:

first matched is "One". the second is here "Two "
and here are in second line " Three " "Four".

Я знаю, что могу сделать это с несколькими комбинацией команд. Но я ищу одну команду и не используя это для нескольких время. например: ниже команды

grep -oP '"[^"]*"' file | grep -oP '[^"]*'

Как я могу достигать/печатать всех своих шаблонов просто с помощью одной команды?

Ответ на комментарии: это не важно для меня к удалению пробела вокруг подобранного шаблона в паре кавычек, но было бы лучше, если команда поддерживает его также. и также мои файлы содержат вложенные кавычки как "foo "bar" zoo". И все заключенные в кавычки слова находятся в отдельных строках, и они не расширены до много строк.

Заранее спасибо.

5
задан 12 November 2014 в 21:32

5 ответов

В первую очередь, Ваш grep -Po '"\K[^"]*' file идея перестала работать потому что grep видит обоих "One" и ". the second is here" как являющийся в кавычках. Лично, я, вероятно, просто сделал бы

$ grep -oP '"[^"]+"' file | tr -d '"'
One
Two 
 Three 
Four

Но это - две команды. Чтобы сделать это с единственной командой, Вы могли использовать один из:

  1. Perl

    $ perl -lne '@F=/"\s*([^"]+)\s*"/g; print for @F' file 
    One
    Two 
    Three 
    Four
    

    Здесь, @F массив содержит все соответствия regex (кавычка, сопровождаемая как многие не -" как возможный до следующего "). print for @F просто означает, "печатают каждый элемент @F.

  2. Perl

    $ perl -F'"' -lne 'for($i=1;$i<=$#F;$i+=2){print $F[$i]}' file 
    One
    Two 
     Three 
    Four
    

    Для удаления продвижения/конечных пробелов из каждого соответствия используйте это:

    perl -F'"' -lne 'for($i=1;$i<=$#F;$i+=2){$F[$i]=~s/^\s*|\s$//; print $F[$i]}' file 
    

    Здесь, Perl ведет себя как awk. -a переключитесь заставляет это автоматически разделять входные строки на поля на символе, данном -F. Так как я дал его ", поля:

    $ perl -F'"' -lne 'for($i=0;$i<=$#F;$i++){print "Field $i: $F[$i]"}' file 
    Field 0: first matched is 
    Field 1: One
    Field 2: . the second is here
    Field 3: Two 
    Field 0: and here are in second line
    Field 1:  Three 
    Field 2: 
    Field 3: Four
    Field 4: .
    

    Поскольку мы ищем текст между двумя последовательными разделителями полей, мы знаем, что хотим каждое второе поле. Так, for($i=1;$i<=$#F;$i+=2){print $F[$i]} распечатает тех, мы заботимся о.

  3. Та же идея, но в awk:

    $ awk -F'"' '{for(i=2;i<=NF;i+=2){print $(i)}}' file 
    One
    Two 
     Three 
    Four
    
7
ответ дан 17 November 2019 в 10:58

Ключ в том, чтобы использовать кавычки в вашем выражении. Трудно сделать это с помощью одной команды grep. Вот одна строчка perl:

perl -0777 -nE 'say for /"(.*?)"/sg' file

, которая отбирает весь ввод и печатает захваченную часть совпадения. Он будет работать, даже если внутри кавычек будет новая строка, хотя тогда будет трудно разделить элементы с символами новой строки и без них. Чтобы помочь с этим, используйте другой символ в качестве разделителя выходных записей, например, нулевой символ

perl -0777 -lne 'print for /"(.*?)"/sg} BEGIN {$\="\0"' <<DATA | od -c
blah "first" blah "second
quote with newline" blah "third"
DATA
0000000   f   i   r   s   t  \0   s   e   c   o   n   d  \n   q   u   o
0000020   t   e       w   i   t   h       n   e   w   l   i   n   e  \0
0000040   t   h   i   r   d  \0
0000046
2
ответ дан 17 November 2019 в 10:58

Это могло быть возможно с ниже grep один лайнер, и я предположил, что Вы сбалансировали кавычки.

grep -oP '"\s*\K[^"]+?(?=\s*"(?:[^"]*"[^"]*")*[^"]*$)' file

Пример:

$ cat file
first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".
$ grep -oP '"\s*\K[^"]+?(?=\s*"(?:[^"]*"[^"]*")*[^"]*$)' file
One
Two
Three
Four

Другое решение для получения по запросу волос через глагол PCRE (*SKIP)(*F) ,

$ grep -oP '[^"]+(?=(?:"[^"]*"[^"]*)*[^"]*$)(*SKIP)(*F)|\s*\K[^"]+(?=\b\s*)' file
One
Two
Three
Four
1
ответ дан 17 November 2019 в 10:58

Используя sed:

sed 's/[^"]*"\([^"]\+\)"[^"]*/\1\n/g' file

[^"]*

^ в начале [^"]* ... средства, которым не должны соответствовать символы, перечисленные в классе символов (только единственное соответствие "). * средства " может произойти нуль или больше раз.

"\([^"]\+\)"

Все внутри \(...\) группа соответствия. Первый символ за пределами группы соответствия является начать матчем. Класс символов [^"] следует (Это соответствует каждому символу кроме "). Квантор \+ средства там должны быть по крайней мере одним символом между кавычками ("...") в Вашем входном файле. Затем \), конец группы соответствия. Эта группа соответствия может быть доступом своим индексом через \1.

Последняя часть [^"]* совпадает с первой частью, которая соответствует всему до следующего ".

0
ответ дан 17 November 2019 в 10:58

Альтернативный подход с Python, который не требует регулярных выражений (хотя и не совсем надежный), заключается в обработке каждой строки в вашем текстовом файле символ за символом.

Основная идея того, как это работает: если мы видим двойную кавычку и флаг не поднимается - поднимите флаг, а если мы увидим его снова и флаг поднимется - уменьшите флаг. Когда флаг поднят - мы знаем, что мы в двойных кавычках, поэтому мы можем хранить последующие символы. Как только флаг опустится, выведите то, что мы прочитали.

#!/usr/bin/env python
from __future__ import print_function
import sys

flag=False
quoted_string=[]
for line in sys.stdin:
    for char in line.strip():
        if char == '"':
           if flag:
               flag=False
               if quoted_string:
                  print("".join(quoted_string))
                  quoted_string=[]
           else:
               flag=True
               continue 
        if flag:
           quoted_string.append(char)

И тестовый прогон:

$ cat input.txt
first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".

$ ./get_quoted_words.py < input.txt                                                                                      
One
Two 
 Three 
Four
0
ответ дан 17 November 2019 в 10:58

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

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