Как использовать переменные awk в регулярных выражениях? - программирование
Подтвердить что ты не робот

Как использовать переменные awk в регулярных выражениях?

У меня есть файл с именем domain, который содержит некоторые домены. Например:

google.com
facebook.com
...
yahoo.com

И у меня есть другой файл с именем сайт, который содержит URL-адреса и номера сайтов. Например:

image.google.com   10
map.google.com     8
...
photo.facebook.com  22
game.facebook.com   15
..

Теперь я собираюсь подсчитать номер URL-адреса, который имеет каждый домен. Например: google.com имеет 10 + 8. Поэтому я написал awk script следующим образом:

BEGIN{
  while(getline dom < "./domain" > 0) {
    domain[dom]=0;
  }
  for(dom in domain) {
    while(getline < "./site" > 0) {
      if($1 ~/$dom$)   #if $1 end with $dom {
        domain[dom]+=$2;
      }
    }
  }
}

Но код if($1 ~/$dom$) не работает, как я хочу. Потому что переменная $dom в регулярном выражении объяснялась буквально. Итак, первый вопрос:

Можно ли использовать переменную $dom в регулярном выражении?

Затем, поскольку я новичок в написании script

Есть ли лучший способ решить проблему, которая у меня есть?

4b9b3361

Ответ 1

awk может сопоставляться с переменной, если вы не используете маркеры // regex.

if ( $0 ~ regex ){ print $0; }

В этом случае создайте требуемое регулярное выражение в виде строки

regex = dom"$"

Затем сопоставьте с переменной regex

if ( $1 ~ regex ) {
  domain[dom]+=$2;
}

Ответ 2

Прежде всего, переменная dom not $dom - рассмотрите $ как оператор для извлечения значения номера столбца, хранящегося в переменной dom

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

Вам нужна функция match(), где второй аргумент может быть строкой, которая рассматривается как регулярное выражение:

if (match($1, dom "$")) {...}

Я бы закодировал решение вроде:

awk '
  FNR == NR {domain[$1] = 0; next}
  {
    for (dom in domain) {
      if (match($1, dom "$")) {
        domain[dom] += $2
        break
      }
    }
  }
  END {for (dom in domain) {print dom, domain[dom]}}
' domain site 

Ответ 3

Один из способов: awk script:

BEGIN {
    FS = "[. ]"
    OFS = "."
}

FNR == NR {
    domain[$1] = $0
    next
}

FNR < NR {
    if ($2 in domain) {
        for ( i = 2; i < NF; i++ ) {
            if ($i != "") {
                line = (line ? line OFS : "") $i
            }
        }
        total[line] += $NF
        line = ""
    }
}

END {
    for (i in total) {
        printf "%s\t%s\n", i, total[i]
    }
}

Выполнить как:

awk -f script.awk domain.txt site.txt

Результаты:

facebook.com    37
google.com  18

Ответ 4

Вы явно хотите прочитать файл site один раз, а не один раз для записи в domain. Фиксирование этого, однако, тривиально.

В равной степени переменные в awk (кроме полей $0.. $9 и т.д.) не имеют префикса $. В частности, $dom - номер поля, идентифицированный переменной dom (обычно это будет 0, поскольку строки домена не преобразуются ни в какое другое число).

Я думаю, вам нужно найти способ получить домен из данных, считанных из файла site. Я не уверен, что вам нужно иметь дело с сайтами с доменами страны, такими как bbc.co.uk, а также сайтами в GTLD (google.com и т.д.). Предполагая, что вы не имеете дело с доменами страны, вы можете использовать это:

BEGIN {
    while (getline dom < "./domain" > 0) domain[dom] = 0
    FS = "[ .]+"
    while (getline  < "./site" > 0)
    {
        topdom = $(NF-2) "." $(NF-1)
        domain[topdom] += $NF          
    }
    for (dom in domain) print dom "  " domain[dom]
}

Во втором цикле while есть поля NF; $NF содержит счетчик, а $1.. $(NF-1) содержат компоненты домена. Таким образом, topdom заканчивается тем, что содержит имя верхнего домена, которое затем используется для индексации в массив, инициализированный в первом цикле.

Учитывая данные в вопросе (минус линии точек), выход:

yahoo.com  0
facebook.com  37
google.com  18

Ответ 5

Проблема ответов выше состоит в том, что вы не можете использовать "метасимволы" (например,\& lt; для границы слова в начале слова), если вы используете строку вместо регулярного выражения /.../. Если у вас есть домен xyz.com и два сайта ab.xyz.com и cd.prefix_xyz.com, номера двух записей сайта будут добавлены на xyz.com

Вот решение с использованием awk pipe и команды sed: ...

for(dom in domain) {
    while(getline < "./site" > 0) {
        # let sed replaces occurence of the domain at the end of the site
        cmd = "echo '" $1 "' | sed 's/\\<'" dom "'$/NO_VALID_DOM/'"
        cmd | getline x
        close(cmd)
        if (match(x, "NO_VALID_DOM")) { 
          domain[dom]+=$2;
        }
    }
    close("./site") # this misses in original code
}

...