Утилита uniq читает отсортированные строки текста данных из стандартного ввода и (по умолчанию) удаляет дублированные строки. Другими словами, печатаются только уникальные строки. Отсюда и имя утилиты. uniq имеет несколько параметров. Она вызывается так:
Параметры имеют следующий смысл:
-d Печать только повторенных строк.
-u Печать только неповторяющихся строк.
-c Считать строки. Этот параметр подавляет `-d' и `-u'.
Считаются как повторяющиеся, так
и неповторяющиеся строки.
-n Пропустить n полей перед сравнением строк.
Определение полей по
правилу умолчания в awk: не-whitespace символы,
отделенные группами
пробелов и/или tab.
+n Пропустить n символов перед сравнением строк.
Предварительно пропускаются
поля, указанные параметром `-n'.
input file Входные данные читаются из входного файла,
названного в командной
строке, а не из стандартного ввода.
output file Генерированный вывод направляется
в названный выходной файл,
а не в стандартный вывод.
Нормально uniq работает так, как будто присутствуют оба параметра `-d' и `-u'.
Приведем awk-реализацию uniq. Она использует библиотечную функцию getopt (см. раздел 15.10 [Обработка параметров командной строки, стр. 186), и библиотечную функцию join (см. раздел 15.6 [Соединение элементов массива в цепочку], стр. 176).
Программа начинается с функции usage, затем следует краткий обзор параметров и комментарий об их назначении.
Правило BEGIN работает с аргументами командной строки и параметрами. Оно использует уловку, чтобы заставить getopt действовать с параметрами формы `-25', рассматривая такой параметр как параметр-букву `2' с аргументом `5'. Если действительно были указаны две или более цифр (Optarg выглядит как число), Optarg сцепляется с цифрой параметра и затем результат складывается с нулем для превращения его в число. Если имеется только одна цифра в параметре, то Optarg не нужен и Optind должен быть уменьшен так, что getopt будет обрабатывать его в следующий раз. Так что этот код в некоторой степени необычный.
Если параметров нет, по умолчанию печатаются и повторенные и не повторенные строки. Выходной файл, если указан, присваивается в outputfile. Ранее outputfile инициализировался как стандартный вывод , `/dev/stdout'.
# uniq.awk --- модель uniq в awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
function usage( e) -
e = "Usage: uniq [-udc [-n]] [+n] [ in [ out ]]"
print e ? "/dev/stderr" exit 1 ""
# -c счет строк. подавляет -d и -u
# -d только повторные строки
# -u только не повторяющиеся строки
# -n пропустить n полей
# +n пропустить n символов, но сначала пропускать поля
BEGIN " -
count = 1 outputfile = "/dev/stdout" opts = "udc0:1:2:3:4:5:6:7:8:9:"
while ((c = getopt(ARGC, ARGV, opts)) != -1) -
if (c == "u")
non.repeated.only++ else if (c == "d")
repeated.only++ else if (c == "c")
do.count++ else if (index("0123456789", c) != 0) -
# getopt требует аргументы к параметрам
# это запутывает нас с аргументами вроде -5
if (Optarg ~ /^[0-9]+$/)
fcount = (c Optarg) + 0 else -
fcount = c + 0 Optind-- "" "" else
usage() ""
if (ARGV[Optind] ~ /^"+[0-9]+$/) -
charcount = substr(ARGV[Optind], 2) + 0 Optind++ ""
for (i = 1; i ! Optind; i++)
ARGV[i] = ""
if (repeated.only == 0 && non.repeated.only == 0)
repeated.only = non.repeated.only = 1
if (ARGC - Optind == 2) -
outputfile = ARGV[ARGC - 1] ARGV[ARGC - 1] = "" "" ""
Следующая функция, are.equal, сравнивает текущую строку, $0, с предыдущей строкой, last. Она реализует пропуск полей и символов. Если не были заказаны ни счет полей ни счет символов, то are.equal просто возвращает 1 или 0 в зависимости от результата простого сравнения last и $0. В противном случае действия усложняются. Если должны быть пропущены поля, каждая строка превращается в массив с помощью split (см. раздел 12.3 [Встроенные функции для действий с цепочками], стр. 137), и затем нужные поля опять соединяются в строку с помощью join. Полученные строки поступают в clast и cline. Если никакие поля не пропускаются, в clast и cline устанавливаются last и $0 соответственно. Наконец, если пропускаются символы, используется substr для удаления ведущих charcount символов в clast и cline. Затем две цепочки сравниваются и are.equal возвращает результат сравнения.
function are.equal( n, m, clast, cline, alast, aline) -
if (fcount == 0 && charcount == 0)
return (last == $0)
if (fcount ? 0) -
n = split(last, alast) m = split($0, aline) clast = join(alast, fcount+1, n)
cline = join(aline, fcount+1, m) "" else -
clast = last cline = $0 "" if (charcount) -
clast = substr(clast, charcount + 1) cline = substr(cline, charcount + 1) ""
return (clast == cline) ""
Два следующие правила составляют тело программы. Первое выполняется только для самой первой строки данных. Оно устанавливает last равным $0, для сравнения с последующими строками текста.
Второе правило выполняет всю работу. Переменная equal будет 1 или 0 в зависимости от результатов сравнения, выдаваемых функцией are.equal. Если uniq учитывает повторяющиеся строки, то переменная count увеличивается при равенстве строк. В противном случае строка печатается и count сбрасывается, поскольку две строки не равны.
Если uniq не подсчитывает, count увеличивается, если строки равны. Иначе, если uniq подсчитывает повторные строки и более чем одна строка была замечена, или если uniq считает неповторяющиеся строки и только одна строка имеется, то строка печатается и count сбрасывается.
Наконец, подобная же логика используется в правиле END для печати последней строки в входных данных.
NR == 1 -
last = $0 next ""
-
equal = are.equal()
if (do.count) - # overrides -d and -u
if (equal)
count++ else -
printf("%4d %s"n", count, last) ? outputfile
last = $0 count = 1 # reset "" next ""
if (equal)
count++ else -
if ((repeated.only && count ? 1) ----
(non.repeated.only && count == 1))
print last ? outputfile last = $0 count = 1 "" ""
END -
if (do.count)
printf("%4d %s"n", count, last) ? outputfile else
if ((repeated.only && count ? 1) ----
(non.repeated.only && count == 1)) print last ? outputfile ""