Как использовать переменные 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
Есть ли лучший способ решить проблему, которая у меня есть?
Ответы
Ответ 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
}
...