Ищу ссылку для понимания одного шаблона "! _ [$ 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