Оценка команды с помощью Awk

Проблема в том, что: у меня есть разные текстовые файлы, в которых прописана временная метка и IP-адрес для каждого вредоносного пакета, поступающего на сервер. Что я хочу сделать, так это создать еще один текстовый файл, который показывает для каждого IP-адреса первый раз, когда приходит пакет вредоносного ПО.

В общем, я хочу сделать что-то вроде этого:

for every  line in file.txt
 if (ip is not present in list.txt)
 copy timestamp and ip in list.txt

Я использую awk для этого. Основная проблема это "если ip отсутствует в list.txt". Я делаю это:

 {    a=$( grep -w "$3" list.txt | wc -c );
    if ( a == 0 )
   {
     #copy timestamp and ip in list.txt
   }

(я использую $3, потому что IP-адрес находится в третьем столбце исходного файла)

Я не знаю, как заставить awk оценить функцию grep. Я также пробовал с обратными кавычками, но это не сработало. Кто-нибудь может подсказать?

Я тестирую свой скрипт на тестовом файле следующим образом:

10  192.168.1.1
11  192.168.1.2
12  192.165.2.4
13  122.11.22.11    
13  192.168.1.1
13  192.168.1.2
13  122.11.22.11
14  122.11.22.11
15  122.11.22.11
15  122.11.22.144
15  122.11.2.11
15  122.11.22.111

Что я должен получить:

10  192.168.1.1
11  192.168.1.2
12  192.165.2.4
13  122.11.22.11    
15  122.11.22.144
15  122.11.2.11
15  122.11.22.111

Благодаря вашей помощи мне удалось создать сценарий, который соответствует моим потребностям:

awk '
FILENAME == ARGV[1] {
    ip[$2] = 1
    next
}
! ($2 in ip) {
    print $1, $2 >> ARGV[1]
    ip[$2] = 1
}
' list.txt file.txt 

person papafe    schedule 12.10.2011    source источник
comment
можете ли вы просто дать некоторые образцы данных и ожидаемый результат?   -  person Kent    schedule 12.10.2011


Ответы (4)


Но на самом деле то, что вы хотите сделать, это заставить awk сначала прочитать файл list.txt, а затем обработать другой файл с данными list.txt в памяти. Это позволит вам избежать вызова system() для каждой строки.

Я предполагаю, что ip находится в 1-м столбце list.txt.

Когда вы говорите copy timestamp and ip in list.txt, я предполагаю, что вы хотите добавить некоторую информацию из текущей строки файла file.txt в файл list.txt.

awk '
    FILENAME == ARGV[1] {
        ip[$1] = 1
        next
    }
    ! ($3 in ip) {
        print $3, $(whatevever_column_holds_timestamp) >> ARGV[1]
    }
' list.txt file.txt

Учитывая образец файла и упрощенные требования к обновлению вашего вопроса:

awk '! seen[$2]++' filename

приведет к результатам, которые вы видели. Эта программа awk напечатает строку, если IP-адрес еще не был виден.

person glenn jackman    schedule 12.10.2011
comment
Теоретически это кажется хорошим решением для моих нужд. Я пытался использовать его, но list.txt в конце концов является копией file.txt, но я не знаю, почему - person papafe; 12.10.2011
comment
Я делал некоторые предположения о формате ваших файлов. Если у меня неправильные номера столбцов, вам придется обновить. - person glenn jackman; 12.10.2011
comment
Спасибо за помощь! Я не могу использовать упрощенные сценарии, потому что мне нужно запустить их для нескольких исходных файлов. Что касается первого сценария, который вы опубликовали, я фактически изменил номера столбцов. Итак, следуя txt файлам, выложенным в обновлении, я поставил: $2 в 3-й строке, $2 в 6-й строке, а 7-я строка стала print $1, $2 .... Но, похоже, это не работает. Завтра попробую еще раз, может я какую-то глупую ошибку делаю! - person papafe; 13.10.2011
comment
конечно, вы можете: если вы хотите извлечь только уникальные IP-адреса из многих файлов, просто выполните awk '!seen[$2]++' file1 file2 ... > all.uniq; или если вы хотите извлечь уникальные IP-адреса только из каждого файла, тогда for f in file1 file2 ...; do awk '...' "$f" > "$f.uniq"; done - person glenn jackman; 13.10.2011
comment
Теперь я понял, в чем проблема с первым выложенным вами скриптом. Он создает массив ip из list.txt перед обработкой file.txt. Вместо этого он должен обновлять массив по мере обработки файла. Таким образом, строка ip[$2] = 1 должна быть помещена после строки печати. Таким образом, скрипт работает так, как я ожидал. Большое спасибо! - person papafe; 13.10.2011

Интерпретируя вопрос как «Как я могу оценить статус команды из awk?», просто используйте system.

{
  if( system( "cmd" ) == 0 ) {
    # the command succeeded
  {
}

Итак, в вашем случае просто выполните:

{
  if( system( "grep -w \"" $3 "\" list.txt > /dev/null " ) == 0 ) {
    ...
  }
}

Однако вы, возможно, захотите пересмотреть свой подход к проблеме. Каждый раз Grepping требует больших вычислительных ресурсов, и есть лучшие способы решить эту проблему. (Например, прочитайте list.txt один раз в массив.)

Также обратите внимание, что вам не нужно использовать wc. grep терпит неудачу, если он не соответствует строке. Используйте возвращаемое значение, а не синтаксический анализ вывода.

person William Pursell    schedule 12.10.2011
comment
Или используйте опцию -q для grep вместо подробного перенаправления вывода в /dev/null. - person Chris Wesseling; 12.10.2011
comment
@CharString Опция '-q' для grep не переносима. Многие реализации grep захлебнутся от этого. (Это может быть спорным вопросом, так как я почти уверен, что -w также непереносимо, но я думаю, что это хорошая привычка избегать непереносимых функций, где это возможно.) - person William Pursell; 12.10.2011
comment
Хм, справочная страница GNU grep говорит (-q указано в POSIX). Я согласен избегать непереносимых функций. изменить: также говорится, что в сценариях переносимой оболочки следует избегать как -q, так и -s. - person Chris Wesseling; 12.10.2011
comment
Спасибо, это именно то, что мне было нужно. Я попробую и дам вам знать. Я также знал, что grepping неудобен, но я немного торопился, поэтому я выбрал быстрый способ (это потому, что я не знаю - person papafe; 12.10.2011
comment
синтаксис awk так хорошо использовать массивы!). Извините за двойные комментарии, но у меня были некоторые проблемы! - person papafe; 12.10.2011

Это сохранит результат выполнения в переменную a

BEGIN {  } 
{
"grep -w \"$3\" list.txt | wc -c" | getline a
print a
}
END   {}
person bvk256    schedule 12.10.2011
comment
Вы имеете в виду "grep -w \"" $3 "\" list.txt | wc -c" | getline a - person Chris Wesseling; 12.10.2011

Вы хотите использовать getline:

BEGIN {
    "date" | getline current_time
     close("date")
     print "Report printed on " current_time
}

Это берет вывод date и помещает его в переменную current_time. Вы должны быть в состоянии сделать то же самое с вашим grep | туалет -л.

person Mando Escamilla    schedule 12.10.2011