2015-04-10 4 views
0

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

gfdg "jkfgh" "jkfd fdgj fd-" ghjhgj 
gfggf "kfdjfdgfhbg" "fhfghg" jhgj 
jhfjhg "dfgdf" fgf 
fgfdg "dfj jfdg jhfgjd" "hfgdh jfdhgd jkfghfd" hgjghj 

И я хочу, чтобы напечатать только следующее:

"jkfgh" "jkfd fdgj fd-" 
"kfdjfdgfhbg" "fhfghg" 
"dfgdf" 
"dfj jfdg jhfgjd" "hfgdh jfdhgd jkfghfd" 

я получил ответ с помощью этой команды:

awk -F'"' '{for (i=2;i<5;i+=2) printf "%s%s%s%s", FS, $i, FS, (i>5-2?"\n":" ")}' sample.txt 

теперь у меня есть добавить ' ' на мой вопрос. то есть мой текст может быть внутри ' ', а также " ". пример ниже:

gfdg "jkfgh" "jkfd fdgj fd-" ghjhgj 
gfggf "kfdjfdgfhbg" "fhfghg" jhgj 
jhfjhg "dfgdf 'ffdg' gfd" "dgffd 'fdg'"fgf 
fgfdg 'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' hgjghj 

я хотел бы получить следующий результат:

"jkfgh" "jkfd fdgj fd-" 
"kfdjfdgfhbg" "fhfghg" 
"dfgdf 'ffdg' gfd" "dgffd 'fdg'" 
'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' 

может кто-то пожалуйста, помогите мне?

+0

Как насчет других инструментов, таких как Perl? –

+0

@karthikmanchala Я использовал команду выше. это работает только для "", и если я изменю разделитель полей на -F "", это будет работать и для '', но я хочу, чтобы как одиночные, так и двойные кавычки работали вместе. то я использовал -F "^ '| \" ", чтобы иметь оба разделителя поля, но не очень хороший результат. –

+0

Вы можете использовать несколько разделителей в awk, например' awk -F' [/ =] '' {print $ 3 " \ t "$ 5" \ t "$ 8} '' –

ответ

2

Простейшая вещь, вероятно, пойти один символ за один раз:

$ cat tst.awk 
BEGIN { FS="" } 
{ 
    rec = "" 
    for (i=1;i<=NF;i++) { 
     if (($i=="\"") && !inSq) { 
      rec = rec (inDq ? $i : (rec ? " " : "")) 
      inDq = !inDq 
     } 
     else if (($i=="'") && !inDq) { 
      rec = rec (inSq ? $i : (rec ? " " : "")) 
      inSq = !inSq 
     } 

     if (inDq || inSq) { 
      rec = rec $i 
     } 
    } 
    print rec 
} 

$ awk -f tst.awk file 
"jkfgh" "jkfd fdgj fd-" 
"kfdjfdgfhbg" "fhfghg" 
"dfgdf 'ffdg' gfd" "dgffd 'fdg'" 
'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' 

Там может быть RE, вы можете использовать с FPAT в простака, но вместо этого я не могу быть обеспокоен, чтобы думать об этом. Вышеизложенное можно заставить работать, даже если в ваших цитатах есть новые строки, в том числе путем чтения всего файла в виде одной записи с использованием RS='^$' в gawk.

Мне очень нравится ответ Dave Sines '(https://stackoverflow.com/a/29564199/1745001), но думал, что это может быть немного более кратким, так что я массировала его к этому:

$ cat tst.awk 
{ 
    rec = "" 
    while (match($0,/['"]/)) { 
     delim = substr($0,RSTART,1) 
     fldLgth = index(substr($0,RSTART+1),delim) + 1 
     rec  = (rec ? rec " " : "") substr($0,RSTART,fldLgth) 
     $0  = substr($0,RSTART+fldLgth) 
    } 
    print rec 
} 
$ awk -f tst.awk file 
"jkfgh" "jkfd fdgj fd-" 
"kfdjfdgfhbg" "fhfghg" 
"dfgdf 'ffdg' gfd" "dgffd 'fdg'" 
'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' 

Если вам нравится, что то, пожалуйста, примите ответ Дэйва и просто относятся к этому как альтернативная реализация.

+0

Большое спасибо. работает ли он всего два раза подряд? »или« »? –

+2

@hketab Что это значит?Если у вас есть другие случаи, которые вы не зафиксировали в своем размещенном экземпляре, тогда отредактируйте свой вопрос, чтобы показать эти случаи. –

+0

Я отправил ответ на «FPAT»; если вы хотите посмотреть на него, сообщите мне, есть ли с ним проблемы. – mklement0

3
{ 
    a = "" 
    s = $0 
    # while s contains a delimiter (either " or ') 
    while (match(s, /['"]/)) { 
    # save the delimiter 
    c = substr(s, RSTART, 1) 
    # remove up to and including the delimiter 
    s = substr(s, RSTART + 1) 
    # find the matching delimiter 
    i = index(s, c) 
    # append the saved delimiter and the first segment of s to the accumulator 
    a = a " " c substr(s, 1, i) 
    # remove the segment 
    s = substr(s, i + 1) 
    } 
    # print the accumulator (dropping the first space) 
    print substr(a, 2) 
} 
+0

Хороший подход - +1! Хотелось бы, чтобы вы использовали более длинные, более значимые имена переменных, хотя, и я думаю, что есть некоторые промежуточные шаги, которые не помогают ясности, поэтому я изменил его и добавил альтернативную реализацию в конце моего ответа (http: // stackoverflow.com/a/29561731/1745001). –

1

Цитирую - адаптированный - ядро ​​моего ответа на https://stackoverflow.com/a/29513125/45375, где вы спросили по существу один и тот же вопрос (только обфусцированный некоторыми заблуждениями).

Если у вас есть GNU Awk, вы может приблизительной распознавания строк в кавычкахиспользуя специальную FPAT переменной, которая, вместо того, чтобы определять сепаратор для разделения линий с помощью, позволяешь задать регулярное выражение, которое описывает поля (и игнорирует маркеры не признаются в качестве таковых):

gawk -v FPAT="\"[^\"]*\"|'[^']*'" '{ 
    for(i=1;i<=NF;++i) printf "%s%s", $i, (i==NF ? "\n" : " ") 
}' sample.txt 

Это будет работать с одно- и двойными кавычками, но делает не поддержки внедренных кавычкам экранирована из же типа.

Объяснение:

  • FPAT="\"[^\"]*\"|'[^']*'" определяет поля быть либо или одинарные вызова двойных кавычках строки, даже пустые.
  • Обратите внимание, что это автоматически исключает токены с UNKoted на каждой входной линии - они будут не будут отражены в $1, ... и NF.
  • Таким образом, петля for(i=1;i<=NF;++i) уже ограничивается перечислением только соответствующих полей. Поля включают включенные кавычки, как требуется здесь.
+1

Выглядит хорошо '+ 1'. Вы также можете поддерживать пустые поля, просто изменив '+' s на '*' s. FWIW Обычно я пишу печать в цикле как 'printf '% s% s", $ i, (i

+1

@EdMorton: Thanks; хорошая точка re '*' - обновлена. – mklement0

0

Истинные требования окутаны туман путаницы, но тема Надёжных и обобщенно синтаксических анализ разделенных пробельных лексем, которые могут быть двух- или одиночными кавычки является интересным.

Даже если это можно сделать с помощью awk, это громоздко, о чем свидетельствуют существующие ответы;Функции анализа поля не поддерживают прямую поддержку цитируемых строк.

Вот гораздо проще perl решение, которое использует Text::Parsewords module - которые могут или не могут прийти с вашим perl распределения (например, предустановленная на OSX 10.10, но не на Ubuntu 14.04):

perl -MText::Parsewords -lne ' 
    my @flds = Text::ParseWords::parse_line("\\s+", 1, $_); 
    print join(" ", grep /^["\047]/, @flds); 
' sample.txt 
  • Text::ParseWords::parse_line("\\s+", 1, $_) анализирует каждую входную строку ($_) в токенах, на основе пробелов в качестве разделителя, распознающих как одно-, так и двойные кавычки, с поддержка для \ -escaped embedded цитаты того же типа; 1 в качестве второго аргумента указывает, что котировки должны быть сохранено.
  • grep /^["\047]/, @flds спички и возвращает только те жетоны, которые начинаются с " или ' (' представлена ​​в виде последовательности побега \047, поскольку ' не может быть непосредственно встроены в одинарных кавычках оболочки строки).
  • print join(" ", ... соединяет маркеры результата с пробелом в качестве разделителя и печатает результат.

Предостережение: Это решение отличается от требуемого выхода ФП в образце в одном отношении: "dgffd 'fdg'"fgf признан в качестве маркера в целом, а не только "dgffd 'fdg'" префикса.
Если вы действительно хотите только префикс в этом случае, используйте следующее в качестве 2-й линии сценария Perl, но обратите внимание, что делать это означает, что добыча будет работать неправильно с вложенной спасся цитаты:

print join(" ", map { s/^((["\047]).*\2).*/$1/r } grep /^["\047]/, @flds); 
0

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

BEGIN { 
    FS = "" 
} 
{ 
for (CharFromStart=1;CharFromStart<=NF;CharFromStart++) { 
     if ($CharFromStart ~/"|'/) { 
      break 
     } 
    } 
for (CharFromEnd=NF;CharFromEnd>0;CharFromEnd--) { 
     if ($CharFromEnd ~/"|'/) { 
      break 
     } 
    } 
if (CharFromStart <= CharFromEnd) { 
    print ">"substr($0,CharFromStart,(CharFromEnd-CharFromStart+1))"<" 
    } 
else { 
    print "Move along please, nothing to see here" 
    } 
} 

С некоторыми дополненными тестовые данные:

gfdg "jkfgh" "jkfd fdgj fd-" ghjhgj 
gfggf "kfdjfdgfhbg" "fhfghg" jhgj 
jhfjhg "dfgdf 'ffdg' gfd" "dgffd 'fdg'"fgf 
fgfdg 'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' hgjghj 
jhfjhg "dfgdf 'ffdg' gfd" "dgffd 'fdg'" fgf 
jhfjhg "dfgdf   'ffdg ' gfd"  "  dgffd 'fdg'"fgf 
kiuj jajdj "dfgdf   'ffdg ' gfd"  "  dgffd 'fdg'" s fgf 
dslkjflkdsj ldsk gfdkg ;kdsa;lfkdsl f ljflkdsjf l 
ldsfl dsjfhkjds dshfjkhds kdskjfhdskjhf " dsflkdsjflk 
' dlfkjdslfj kdsjflkdslj djlkfjdslkjf 
dskfjds dshfdkjsh dshjkjfhds " 
""" 

Дает:

>"jkfgh" "jkfd fdgj fd-"< 
>"kfdjfdgfhbg" "fhfghg"< 
>"dfgdf 'ffdg' gfd" "dgffd 'fdg'"< 
>'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd'< 
>"dfgdf 'ffdg' gfd" "dgffd 'fdg'"< 
>"dfgdf   'ffdg ' gfd"  "  dgffd 'fdg'"< 
>"dfgdf   'ffdg ' gfd"  "  dgffd 'fdg'"< 
Move along please, nothing to see here 
>"< 
>'< 
>"< 
>"""< 

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

Цикл «вверх» линии, используя $ variablename, чтобы найти первую цитату или апостроф. Цикл «вниз» линии, чтобы найти последнюю цитату или апостроф.

Быстрая проверка того, что хотя бы один был найден, и распечатать подстроку строки от первой цитаты или апострофа до последней и включить последнюю.

Если на линии есть только одна цитата или апостроф, она будет напечатана, но проста, чтобы этого не сделать.

Если цитата или апостроф «неуравновешен», никаких проблем с извлечением (если вы этого не хотите). Встроенные пробелы, табы или такие-то будут оставаться там, где они есть, относительно первой цитаты или апострофа.

0

Простой метод

awk '{$1="";sub(/^ /,"")sub(/fgf/,"")}NR!=3{NF=NF-1}1' file 
    "jkfgh" "jkfd fdgj fd-" 
    "kfdjfdgfhbg" "fhfghg" 
    "dfgdf 'ffdg' gfd" "dgffd 'fdg'" 
    'dfj "jfdg" jhfgjd' 'hfgdh jfdhgd jkfghfd' 
Смежные вопросы