Команда uniq Linux
Команда uniq предназначена для поиска одинаковых строк в массивах текста. При этом с найденными совпадениями пользователь может совершать множество действий — например, удалять их из вывода либо наоборот, выводить только их.
Работа команды осуществляется как с текстовыми файлами (в том числе, записями скриптов), так и с текстом, напечатанным в командной строке терминала.
Синтаксис uniq
Запись команды осуществляется следующим образом:
$ uniq опции файл_источник файл_для_записи
Файл источник указывает откуда надо читать данные, а файл для записи — куда писать результат. Но их указывать не обязательно. В примерах мы будем набирать текст, который нуждается в редактировании, прямо в командную строку терминала, воспользовавшись ещё одной командой — echo, и применив к ней опцию -e. Это будет выглядеть так:
echo -e [текст, слова в котором разделены управляющей последовательностью\\n] | uniq
Эта управляющая последовательность нужна, чтобы указать утилите, что каждое слово выводится в новой строке. Если указано только название файла источника, результат выполнения команды появится прямо в окне терминала. А при наличии выходного файла текст будет напечатан в теле документа.
Опции uniq
У команды uniq есть такие основные опции:
- -u (—unique) — выводит исключительно те строки, у которых нет повторов.
- -d (—repeated) — если какая-либо строка повторяется несколько раз, она будет выведена лишь единожды.
- -D — выводит только повторяющиеся строки.
- —all-repeated[=МЕТОД] — то же самое, что и -D, но при использовании этой опции между группами из одинаковых строк при выводе будет отображаться пустая строка. [=МЕТОД] может иметь одно из трех значений — none (применяется по умолчанию), separate или prepend.
- —group[=МЕТОД] — выводит весь текст, при этом разделяя группы строк пустой строкой. [=МЕТОД] имеет значения separate (по умолчанию), prepend, append и both, среди которых нужно выбрать одно.
Вместе с основными опциями могут применяться дополнительные. Они нужны для более тонких настроек работы команды:
- -f (—skip-fields=N) — будет проведено сравнение полей, начиная с номера, который следует после указанного вместо буквы N. Поля — это слова, хотя, называть их словами в прямом смысле слова нельзя, ведь словом команда считает любую последовательность символов, отделенную от других последовательностей пробелом либо табуляцией.
- -i (—ignore-case) — при сравнении не будет иметь значение регистр, в котором напечатаны символы (строчные и заглавные буквы).
- -s (—skip-chars=N) — работает по аналогии с -f, однако, игнорирует определенное количество символов, а не строк.
- -c (—count) — в начале каждой строки выводит число, которое обозначает количество повторов.
- -z (—zero-terminated) — вместо символа новой строки при выводе будет использован разделитель строк NULL.
- -w (—check-chars=N) — указание на то, что нужно сравнивать только первые N символов в строках.
Примеры использования uniq
Прежде всего следует отметить главную особенность команды uniq — она сравнивает только строки, которые находятся рядом. То есть, если две строки, состоящие из одинакового набора символов, идут подряд, то они будут обнаружены, а если между ними расположена строка с отличающимся набором символов — то не будут поэтому перед сравнением желательно отсортировать строки с помощью sort. Без задействования файлов uniq работает так:
echo -e небо\\nоблака\\nоблака\\nоблака\\nсолнце\\nзвезды | uniq
После команды uniq можно использовать её опции. Вот пример вывода, где не просто удалены повторы, но и указано количество одинаковых строк:
echo -e небо\\nоблака\\nоблака\\nоблака\\nсолнце\\nзвезды | uniq -c
Теперь применим команду к тексту, который находится в файле.
uniq —all-repeated=prepend text-example.txt
Как можно заметить, глядя на снимок экрана, команда вывела в качестве повторяющихся только вторую и третью группу строк.
Причина этого — незаметный глазу символ пробела, который стоит в конце одной из строк первой группы. Нужно быть предельно внимательным при использовании uniq, чтобы получить качественный результат.
Используемая опция —all-repeated=prepend выполнила свою работу — добавила пустые строки в начало, в конец и между группами строк. Теперь попробуем сравнить только первые 5 символов в каждой строке.
echo -e небо исполосовано молниями\\nоблака на небе\\nоблака разогнал ветер\\nоблака закрыли солнце\\nсолнце светит ярко\\nзвезды кажутся огромными | uniq -w5
Как видно на скриншоте, повторяющиеся строки, которые начинались словом «облака», были удалены. Осталась только первая из них. Вывод только уникальных строк с использованием опции -u выглядит так:
echo -e небо\\nоблака\\nоблака\\nоблака\\nсолнце\\nзвезды | uniq -u
Чтобы проигнорировать определенное количество символов в начале одинаковых строк, воспользуемся опцией —skip-chars. В данном случае команда пропустит слово «облака», сравнив слова «перистые» и «белые».
echo -e небо\\nоблака перистые\\nоблака перистые\\nоблака белые\\nсолнце\\nзвезды | uniq —skip-chars=6
А вот наглядная демонстрация отличий при использовании опции —group с разными значениями. both добавило пустые строки как перед текстом, так и после него, а также между группами строк.
echo -e небо\\nоблака\\nоблака\\nоблака\\nсолнце\\nзвезды | uniq —group=both
Тогда как append не добавило пустую строку перед текстом:
echo -e небо\\nоблака\\nоблака\\nоблака\\nсолнце\\nзвезды | uniq —group=append
Выводы
Команда uniq linux пригодится тем, кто часто и много работает с массивами текста, не имея возможности вычитывать их самостоятельно. Следует заметить, что не все версии uniq работают исправно, поэтому иногда результат выдачи может отличаться от ожидаемого.
Свои вопросы относительно использования команды, а также замечания и пожелания оставляйте в комментариях.
Источник
Подсчет одинаковых строк в двух файлах
Скажем, у меня есть два файла, и я хочу узнать, сколько в них одинаковых строк. Например, file1 — это
И file2 содержит
В этом случае ответ должен быть 3 (общие строки — «3», «10» и «5»).
Это, конечно, делается довольно просто с помощью python, например, но мне стало любопытно сделать это из bash (с некоторыми стандартными утилитами или дополнительными вещами, такими как awk или что-то еще). Вот что я придумал:
Это кажется слишком сложным для задачи, поэтому мне интересно, есть ли более простой или элегантный способ достичь того же результата.
P.S. Также было бы неплохо вывести процентное соотношение общей части к количеству строк в каждом файле, но это не обязательно.
UPD: в файлах нет повторяющихся строк
7 ответов
Чтобы найти строки, общие с вашими двумя файлами, используя awk:
Будет выводить 3 10 15
Теперь просто перенаправьте это на wc , чтобы получить количество общих строк:
Объяснение:
Здесь a работает как словарь со значением по умолчанию 0. Когда вы пишете a[$0]++ , вы добавляете 1 к a[$0] , но эта инструкция возвращает предыдущее значение a[$0] (см. разницу между a ++ и ++ a). Таким образом, у вас будет 0 (= ложь) в первый раз, когда вы встретите определенную строку, и 1 (или больше, все еще = истина) в следующий раз.
По умолчанию awk ‘condition’ file — это синтаксис для вывода всех строк, в которых condition истинно.
Также имейте в виду, что массив a[] будет расширяться каждый раз, когда вы встречаетесь с новым ключом. В конце вашего скрипта размером массива будет количество уникальных значений, которые у вас есть во всех ваших входных файлах (в примере OP это будет 9).
Примечание. это решение учитывает дубликаты, т. е. если у вас есть:
awk ‘a[$0]++’ file1 file2 выведет 3 3 3 , а awk ‘a[$0]++’ file1 file2 | wc -l выведет 3
С вашим примером ввода это тоже работает. но если файлы огромны, я предпочитаю другие решения awk:
С вашими входными файлами, приведенная выше строка выводит
Вот пример без awk, который вместо этого использует comm :
comm сравнивает два отсортированных файла. Аргументы 1,2 подавляют уникальные строки, найденные в обоих файлах. На выходе получаются общие строки в отдельных строках. wc -l подсчитывает количество строк.
А при подсчете (очевидно):
Вы также можете использовать команду comm . Помните, что вам нужно сначала отсортировать файлы, которые нужно сравнить:
Из справочных страниц для команды comm: comm — сравнить два отсортированных файла построчно Параметры:
Вы можете делать все с помощью awk:
Чтобы получить процент, работает примерно так:
В своем решении вы можете избавиться от одной кошки:
Как насчет того, чтобы все было красиво и просто .
Это все, что нужно:
Man sort: -n, —numeric-sort — сравнить по числовому значению строки
Man uniq: -d, —repeated — печатать только повторяющиеся строки
Man wc: -l, —lines — вывести счетчики новой строки
Надеюсь это поможет.
ИЗМЕНИТЬ — на один процесс меньше (кредитный мартин):
Источник
[Bash] Поиск строк с общими полями и подсчет количества совпадений
Доброго времени суток!
Нуждаюсь в помощи опытных коллег.
Есть файл такого содержания
- Иван:Петров
- Сергей:Петров
- Виктор:Петров
- Вадим:Петров
- Алексей:Петров
- Станислав:Сидоров
- Вячеслав:Сидоров
- Геннадий:Сидоров
- Владимир:Сидоров
- Арсений:Сидоров
- Константин:Иванов
- Дмитрий:Иванов
- Игорь:Иванов
Мне нужно подсчитать кол-во совпадений второго поля и найти строки в которых кол-во совпадений n меньше заданного N в скрипте (в данном случае 5) и использовать второе поле из этих строк в качестве переменной $var_surname для дальнейших действий.
на выходе нужно получить
Буду благодарен любым подсказкам.
1. идешь while-ом по строке 2. пишешь функцию, подсчета совпадений 3. на каждой итерации, бери фамилию. разделитель двоеточие передавай в функцию поиска из пункта 2 (awk -F ‘:’ ‘
Вырезаешь второе поле man cut или man grep . Сортируешь man sort . Считаешь каждое входение man uniq . Получаешь список «кол-во фамилия». Дальше сам
Как говорит тут один аноним, сразу видно пятизвёздочного. Ну накой awk -F ‘:’ ‘
Спасибо, очень помог) Простое и доступное для меня решение.
cat unionfile | cut -d»:» -f2 | sort | uniq -c > output
sed -i ‘s/^ *//’ output
awk -F ‘[()]’ ‘$1 ( 21.07.19 16:12:49 )
Раз тут идет звездная война, а ТС, как я понимаю, ничего не понимает, то если дать почти готовое решение, это ничего не изменить в балансе темных и светлых сил. «` cut -d: -f2 | sort | uniq -c «` И вообще, причем тут баш, сед, авк и другие. Задача именно на понимание.
Ну ты понял, что я ошибся на счет теба. Успехов.
Возможно для больших объемов данных не пойдет и есть решения изящнее и производительнее, но для моего случая и это сработает.
Это точно будет попроизводительнее перелопачивания на баш.
Хорошо, если так можно сделать в bash, сходу и без лишних телодвижений. Я не в курсе и выдал сходу, по памяти.
Здесь sed-ом сначала удаляю пробелы вначале строки, потом с помощью awk по оставляю только строки где количество совпадений меньше заданного (значение в первом поле ( 21.07.19 17:05:25 )
Мне количество совпадений важно обязательно считать в переменную т.к. я потом произвожу с ней математическое действие. Где ты производишь мат. действия? Если в баше, то читай сразу в баш и фильтруй там.
Мне количество совпадений важно обязательно считать в переменную т.к. я потом произвожу с ней математическое действие.
Где ты производишь мат. действия? Если в баше, то читай сразу в баш и фильтруй там.
Мне количество совпадений важно обязательно считать в переменную т.к. я потом произвожу с ней математическое действие.
Спасибо) Протестировал данное решение тоже, все работает.
Только вот выдает строки с совпадениями n не только те, которые меньше заданного значения, но и которые равны этому значению.
Для простых обработок текста есть специальный язык.
Только вот выдает строки с совпадениями n не только те, …
Ясно, пациент полный ноль в баше.
Раз мы для тебя стараемся, сделай для нас что-нибудь. Например, какая самая популярная фамилия?
Только вот выдает строки с совпадениями n не только те, которые меньше заданного значения, но и которые равны этому значению.
Потому там и 4, а не 5. Но если вам для зачёта надо, то поменяйте le, на lt.
Спасибо за пример, познавательно.
Правда отдельной программой не очень удобно, т.к. этот кусочек обработки является частью более глобального скрипта и хочется все в одном файле уместить.
Не, я не сдаю зачеты, я для своих задач пишу и учусь)
Думаю разобрался бы, выше был пример с -t вместо -e и читал я ранее, что -e это equal, но в любом случае спасибо за поправку.
И вообще всем отписавшимся — спасибо большое за то что подсказали как можно решить задачу разными способами. Добра вам всем. Хорошо, что есть такие отзывчивые люди, которым не лень кому-то менее опытному подсказать)
p.s. ну не полный ноль, скрипты пишу какие-то для себя, повышаю сложность. Но вот с массивами очень плотно пока не работал, знакомлюсь как раз.
На будущее — прочитай Advanced Bash Scripting Guide. Он есть и на русском(правда устаревший перевод), но тебе хватит. Оттуда и будет понятна в чём разница между -lt и -le и где можно обойтись встроенными средствами bash(если переносимость на другие шеллы не планируется), а где надо подключать внешние программы.
Ну а потом — только практика. И чтение чужого кода, да.
Правда отдельной программой не очень удобно
хочется все в одном файле уместить.
ну это так себе хотелка. Не зря индустрия больше полусотни лет разбивает программы на осмысленные части. Но пока твой скрипт не слишком велик программу на awk можно внедрить прямо в него
А вот не соглашусь. Сравните свой код и мой чуть выше. Есть разница? Казалось бы в принципе почти одно и тоже и даже может awk тут будет и быстрее. Но, это пока вам не понадобится потом работать с полученными переменными, особенно если надо будет обрабатывать чуть ли весь ассоциативный массив гораздо дальше, чем можно поместить в этот внедренный код в сложный скрипт.
Но, это пока вам не понадобится потом работать с полученными переменными
Объявление запрошенных ТСом переменных в рамках этого скрипта, думаю, излишне (либо мало данных). Что будет, если попадется 2 фамилии с n ★★★★★ ( 21.07.19 23:40:04 )
Последнее исправление: YAR 21.07.19 23:44:10 (всего исправлений: 2)
Что будет, если попадется 2 фамилии с n ★★★★★ ( 21.07.19 23:57:07 )
смысл тогда в awk
Где удобство и скорость?
По удобству — пусть каждый под задачу выбирает удобный для себя вариант. По скорости — awk будет быстрее на больших объемах данных.
Если у ТСа задача все равно подразумевает использование баша (некие математические операции с переменными — и да, а зачем тут баш?) — это не повод решать все исключительно средствами баша, благо подзадачи можно реализовать различными средствами.
По скорости — awk будет быстрее на больших объемах данных.
Да с чего бы это было б сильно заметно? Оба языка, что bash, что awk работают примерно одинаково — парсят текст, создают дерево разбора и его интерпретируют. Я знаю, что там внутри и даже правил и то и другое. У awk есть хорошее преимущество, когда надо обрабатывать входные строки через regex-ы. Когда же у нас задача разбить входные строки на два поля, то применение awk как правило только доказывает непрофессионализм пишущего.
Страдаю какой-то херней вместо приготовления ужина
Да с чего бы это было б сильно заметно?
Страдаю какой-то херней вместо приготовления ужина
Дык. Аж целых 2 минуты жевало, а с первого коммента — уже более 2 часов. Если вам жалко 2 минут на 121Мб, то перепишите на C. Не удивлюсь, что bash тормозит на генерации ассоциативного индекса.
На всякий случай, напоминаю, что я никогда не говорил, что bash на этой задаче может быть быстрее. Я говорил, что если вам понадобится потом эти данные, то встраивание другого интерпретатора в скрипт неудобно и будет скорее всего медленнее. Ну в самом деле, вот пусть у вас 100M будет по 4 имен у фамилий и только одно 5. Ну прогоните через пайп и while read.
Да с чего бы это было б сильно заметно?
я никогда не говорил, что bash на этой задаче может быть быстрее.
Это не «не сильно заметно». Это не «чуть быстрее». Это разница в 200 раз.
Чем что? 2 последовательных цикла на bash по 3 минуты будут быстрее, чем 0.7 секунды на mawk + 3 минуты на bash? Ну ок.
Дальше можно было не писать:
Когда же у нас задача разбить входные строки на два поля, то применение awk как правило только доказывает непрофессионализм пишущего
Это не «не сильно заметно». Это не «чуть быстрее». Это разница в 200 раз.
Как вы предсказуемы. На реальных данных, когда вообще имеет смысл пользоваться скриптами, разница в долях секунд не заметна.
А вот это уже подлог. Я вам предлагал обработать задачу awk, а потом закачать весь массив через read. Собственно данных уже достаточно, чтобы увидеть, что будет медленнее.
Добрый день. Вы оказались правы, когда продвинулся дальше, то оказалось, что был один неучтенный момент в логике скрипта и понадобилось помимо «фамилии» и кол-ва совпадений извлекать еще и полностью строки удовлетворяющие данному условию.
А вот как это сделать в контексте вашего примера никак не разберусь. Подключать еще один массив?
Справедливости ради, обсуждалось абстрактное условие: «если вдруг. » 🙂
Так как в bash нет многомерных массивов, а их ручная эмуляция весьма кривая как по виду кода так и по скорости, то новые массивы обычно наилучшее решение. Если б не одно но, которое у вас: ассоциативный массив у вас не сохраняет порядок следования входным данным и придётся решать задачу конкретно по ситуации, может даже вначале заполнять данными обычные, а потом отдельно высчитывать с ассоциативными.
Ты занимаешься не тем делом. Ты пытаешься работать с базой данных на скриптовом языке, который задуман как запускалка других программ. У тебя ничего хорошего не получится, как хорошо бы ты не знал этот скриптовый язык для запуска программ. И при этом ты не знаешь этот скриптовый язык.
Это лишь кусочек большого скрипта с ветвлениями и циклами. Мне нужно запускать один файл и чтобы он выполнял всю работу на серверах, принимал разные решения в зависимости от вводных данных, ошибок, которые меняются после каждого последующего запуска. Поэтому я решил, что будет удобнее записывать данные в простые файлы. И при повторном запуске считывать данные с них и выполнять те или иные действия.
Если подключать БД, то вероятно одним скриптом я уже не справлюсь?
Примерно в этом ключе и думал.
Либо с полученной переменной потом в цикле считать все строки, которые ее содержат.
Не надо строить массивы в баше при обработке реальных данных, ты столкнешься с тем, что у баш нет эффективных по скорости и по потреблению памяти структур данных для работы с такими объемами данными.
Поэтому используй баш как запускалку подходящих для этого программ. Не надо считать математику в баш.
Если ты прям не хочешь использовать для этого спец инструменты, хочешь копаться в текстовых файлах, то
Все промежуточные файлы можно скоратить направив через пайпы следующей команде.
Чувствствуешь, что во всем этом практически не используется баш, тупое использование спец-инструментов, и все это есть закат солнца вручную — имитация субд.
Источник