Awk считает строку в двойных кавычках одним токеном и игнорирует пробел между - PullRequest
23 голосов
/ 08 июля 2011

Файл данных - data.txt:

ABC "I am ABC" 35 DESC
DEF "I am not ABC" 42 DESC

cat data.txt | awk '{print $2}'

приведет к "I" вместо строки в кавычках

Как сделать awk таким образом, чтобы он игнорировал пробел внутри кавычки и думал, что это один токен?

Ответы [ 7 ]

8 голосов
/ 04 апреля 2017

Другой альтернативой будет использование переменной FPAT, которая определяет регулярное выражение, описывающее содержимое каждого поля.

Сохраните этот скрипт AWK как parse.awk:

#!/bin/awk -f

BEGIN {
  FPAT = "([^ ]+)|(\"[^\"]+\")"
}
{
  print $2
}

Сделайте его исполняемым с chmod +x ./parse.awk и проанализируйте ваш файл данных как ./parse.awk data.txt:

"I am ABC"
"I am not ABC"
7 голосов
/ 08 июля 2011

Да, это можно сделать красиво в awk. Все поля легко получить без каких-либо серьезных взломов.

(Этот пример работает как в One True Awk , так и в gawk.)

{
  split($0, a, "\"")
  $2 = a[2]
  $3 = $(NF - 1)
  $4 = $NF
  print "and the fields are ", $1, "+", $2, "+", $3, "+", $4
}
5 голосов
/ 08 июля 2011

Попробуйте это:

$ cat data.txt | awk -F\" '{print $2}'
I am ABC
I am not ABC
3 голосов
/ 23 октября 2015

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

В конце концов я наткнулся на ответ Винтермута в другой ветке , и он предоставил хорошее обобщенное решение этой проблемы. Я только что изменил это, чтобы удалить кавычки. Обратите внимание, что вам нужно вызвать awk с -F\" при запуске программы ниже.

BEGIN { OFS = "" } {
    for (i = 1; i <= NF; i += 2) {
        gsub(/[ \t]+/, ",", $i)
    }
    print
}

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

Затем вы можете легко связать другой экземпляр awk для выполнения любой необходимой обработки (просто снова используйте переключатель разделителя полей, -F,).

Обратите внимание, что это может сломаться, если будет указано первое поле - я его не проверял. Если это так, то это легко исправить, добавив оператор if, начинающийся с 2, а не с 1, если первый символ строки - ".

2 голосов
/ 28 мая 2013

Я собрал воедино функцию, которая разделяет $ 0 на массив с именем B. Пробелы между двойными кавычками не действуют как разделители полей. Работает с любым количеством полей, как из кавычек, так и без кавычек. Здесь идет:

#!/usr/bin/gawk -f

# Resplit $0 into array B. Spaces between double quotes are not separators.
# Single quotes not handled. No escaping of double quotes.
function resplit(       a, l, i, j, b, k, BNF) # all are local variables
{
  l=split($0, a, "\"")
  BNF=0
  delete B
  for (i=1;i<=l;++i)
  {
    if (i % 2)
    {
      k=split(a[i], b)
      for (j=1;j<=k;++j)
        B[++BNF] = b[j]
    }
    else
    {
      B[++BNF] = "\""a[i]"\""
    }
  }
}

{
  resplit()

  for (i=1;i<=length(B);++i)
    print i ": " B[i]
}

Надеюсь, это поможет.

0 голосов
/ 16 января 2016

Вот что-то вроде того, что я наконец-то получил, это более общее для моего проекта.Обратите внимание, что он не использует awk.

someText="ABC \"I am ABC\" 35 DESC '1 23' testing 456"
putItemsInLines() {
    local items=""
    local firstItem="true"
    while test $# -gt 0; do
        if [ "$firstItem" == "true" ]; then
            items="$1"
            firstItem="false"
        else
            items="$items
$1"
        fi
        shift
    done
    echo "$items"
}

count=0
while read -r valueLine; do
    echo "$count: $valueLine"
    count=$(( $count + 1 ))
done <<< "$(eval putItemsInLines $someText)"

Какие выходы:

0: ABC
1: I am ABC
2: 35
3: DESC
4: 1 23
5: testing
6: 456
0 голосов
/ 08 июля 2011

Хорошо, если вы действительно хотите все три поля, вы можете получить их, но это займет много времени:

$ cat data.txt | awk -F\" '{print $1 "," $2 "," $3}' | awk -F' ,' '{print $1 "," $2}' | awk -F', ' '{print $1 "," $2}' | awk -F, '{print $1 "," $2 "," $3}'
ABC,I am ABC,35
DEF,I am not ABC,42

По последнему каналу у вас есть все три поля, чтобы делать все, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...