Ищу ссылку для понимания одного шаблона "! _ [$ 0] ++"

Являюсь новичком AWK, используя утилиты GNU, перенесенные в Windows (UNXUtils) и gawk вместо awk. Решение на этом форуме работает как абсолютная магия, и я пытаюсь найти источник, который я могу прочитать, чтобы лучше понять выражение шаблона, предлагаемое в этом решении.

В Выберите уникальные или различные значения из списка в оболочке UNIX script от Dimitre Radoulov, предлагая следующий код

zsh-4.3.9[t]%   awk '!_[$0]++' file

как решение для выбора элементов списка с повторяющимися и перемешанными элементами, перечисляя каждый элемент только один раз.

Я ранее использовал sort | uniq для этого, который отлично работал для небольших тестовых файлов. По моей фактической проблеме (извлечение списка символов компании из архивных данных о книге заказов из Национальной фондовой биржи Индии в течение 16 дней в апреле 2006 года, с 129 миллионами записей в нескольких файлах), размер сортировки стал слишком большим. И uniq устраняет только смежные дубликаты.

Копируя вышеприведенную строку для моего Win-GNU gawk, я использовал

C:\Users\PAPERS\>  cat ..\Full*_Symbols.txt | gawk "!_[$0]++"  | wc -l

946

предполагая, что 129 миллионов записей относятся к 946 различным фирмам, что является ОЧЕНЬ разумным ответом. И это заняло менее 5 минут на моей скромной машине Windows, после нескольких часов попыток SORT избаловало меня.

Посмотрел на все тексты awk, которые у меня есть, и немного искал в Интернете, и хотя для части шаблона объяснение, почему это сработало, ясно (! служит как NOT, $0 - это вся текущая запись), для подчеркивания _ Я не могу найти никаких объяснений и видел ++ в примерах только как "обновить счетчик на 1."

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

Ответ 1

Это действительно очень умно!

Он создает ассоциативный массив (это означает, что "index" может быть чем угодно, а не просто числом). Если элемент не существует (равен нулю), он создается (путем его увеличения), а когда есть соответствие awk, выполняется действие по умолчанию (которое предназначено для печати строки ввода). После того, как значение найдено, _[$0] будет отличным от нуля, поэтому, если одно и то же значение встречается снова, выражение ложно и ничего не печатается.

Я думаю, что подчеркивание - это просто имя переменной "vanilla" (вам нужно имя для вашего массива, а подчеркивание - как действительный как monkey, но более "анонимный". Классика!

Ответ 2

Объяснение _ отсутствует, за исключением того, что некоторые люди считают его умным, чтобы запутать свой код, используя символ подчеркивания как имя переменной, в данном случае массив. Как и в C, имена переменных в awk могут начинаться с любой буквы или подчеркивания, но очевидно, что намерение состоит не в том, чтобы они ТОЛЬКО были подчеркиванием - это просто смешно!

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

awk '!seen[$0]++'

Вышеупомянутый массив с именем seen индексируется текстом текущей строки. При первом тестировании массив у каждого индекса имеет нулевое значение, при повторном тестировании с той же строкой он имеет значение 1 и т.д. Из-за пост-приращения. Поэтому отрицание этого значения истинно только тогда, когда первое вхождение данной строки видно на входе и поэтому отбрасывает последующие вхождения.

Ответ 3

Другим способом эта команда может быть расширена следующим образом:

awk '{if (array[$0]==0) {array[$0]+=1;print}}' 

Вы можете понимать как:

_ represents associative array named "array"

!_[$0]  represents (array[$0]==0)

_[$0]++  represents array[$0]+=1

Ответ 4

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

Итак, я разделил его и изучил, используя некоторые тесты. _[$0] изменяется на A[$0]
!A[$0]++ станет Проверьте, нет ли массива A[$0] не ! true, и напечатайте строку, если это не так, поскольку оно не имеет значения по умолчанию, а действие по умолчанию awk - распечатать строку.
После теста добавьте 1 в массив с A[$0]++= A[$0]=A[$0]+1. При ++ позади массива приращение выполняется после теста.

Итак, !A[$0]++ может быть изменено на:

{if (!A[$0]++) print $0}

и некоторый дополнительный информационный текст

{if (!A[$0]++) print "output="$0; else print "output="}

С этими данными в качестве входных данных

cat file
one
two
three
four
two
five
three
six

Я получаю этот вывод:

awk '{printf "line=%s array=%s ",$0,A[$0]} {if (!A[$0]++) print "output="$0; else print "output="}'
line=one array= output=one
line=two array= output=two
line=three array= output=three
line=four array= output=four
line=two array=1 output=
line=five array= output=five
line=three array=1 output=
line=six array= output=six

С информацией.

awk '{printf "line=%s array=%s ",$0,A[$0]} {if (!A[$0]++) print "output="$0; else print "output="}'
line=one array= output=one          # line is `one` and since its not found before array is blank (same as 0) and not true, print the line
line=two array= output=two          # line is `two` and since its not found before array is blank (same as 0) and not true, print the line
line=three array= output=three      # line is `threw` and since its not found before array is blank (same as 0) and not true, print the line
line=four array= output=four        # line is `four` and since its not found before array is blank (same as 0) and not true, print the line
line=two array=1 output=            # line is `two` and its found before giving array 1 and true, do not print the line
line=five array= output=five        # line is `five` and since its not found before array is blank (same as 0) and not true, print the line
line=three array=1 output=          # line is `three` and its found before giving array 1 and true, do not print the line
line=six array= output=six          # line is `six` and since its not found before array is blank (same as 0) and not true, print the line

поэтому вторая строка с two и three не будет напечатана.

Использование исходного выражения в данных дает только уникальное значение:

awk '!_[$0]++' file
one
two
three
four
five
six

Чтобы получить все дубликаты:

awk '_[$0]++'
two
three