Подтвердить что ты не робот

Awk рассматривает двойную кавычку как один токен и игнорирует пространство между

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

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

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

приведет к "I" вместо строки, цитируемой

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

4b9b3361

Ответ 1

Да, это можно сделать красиво в 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
}

Ответ 2

Попробуйте следующее:

$ cat data.txt | awk -F\" '{print $2}'
I am ABC
I am not ABC

Ответ 3

Я объединил функцию, которая перераспределяет $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]
}

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

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

#!/bin/awk -f

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

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

"I am ABC"
"I am not ABC"

Ответ 6

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

$ 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

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

Ответ 7

Вот что-то вроде того, что я, наконец, получил работу, более общую для моего проекта. Обратите внимание, что он не использует 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