Невозможно найти имена, которые содержат три семерки в случайном порядке с помощью AWK/Python/Bash.

Мне нужно найти имена, которые содержат три числа 7 в случайном порядке.

Моя попытка

Нам нужно найти имена, которые не содержат семь

ls | grep [^7]

Затем мы могли бы удалить эти совпадения со всего пространства.

ls [remove] ls | grep [^7]

Проблема в моем псевдокоде начинает быстро повторяться.

Как найти имена, содержащие три семерки в случайном порядке, с помощью AWK/Python/Bash?

[edit] Имя может содержать любое количество букв и содержать слова из трех семерок.


person Léo Léopold Hertz 준영    schedule 12.03.2009    source источник
comment
Можете ли вы привести несколько примеров имен файлов - это поможет понять, какие из них должны совпадать, а какие нет.   -  person Andy    schedule 12.03.2009
comment
@Andy: Примеры имен файлов, которые должны совпадать: d41d8Zcd978fABe98009978ecf8427e, 7d41d8CoA00b204e7980U0998ecf8427e и 77d41d8CD98f00204E9800998Ecf8427e.   -  person Léo Léopold Hertz 준영    schedule 12.03.2009
comment
@Andy: Примеры имен файлов, которые не должны совпадать: d41d8cd98f00b2049800998eCf842e, d41d8cd98F00b204e9800998ecf8427e и 77d41d8cd98f00b204E79800998ecf8427e.   -  person Léo Léopold Hertz 준영    schedule 12.03.2009


Ответы (5)


Что-то вроде этого:

printf '%s\n' *|awk -F7 NF==4
person Dimitre Radoulov    schedule 12.03.2009
comment
Аккуратное решение с использованием awk, но замените printf на «ls -1» или просто на «ls» (по умолчанию -1, если вывод представляет собой канал). - person Andrew Dalke; 12.03.2009
comment
Нет, ls — это внешняя команда (дорогой ненужный форк), которая в данном случае не нужна. В большинстве современных оболочек printf является встроенной командой, поэтому она работает быстрее. - person Dimitre Radoulov; 12.03.2009
comment
Быстрее? С двумя файлами я вижу 0,007 с с использованием ls против 0,004 с с printf. Вычтите немного, так как моим таймингам требовалось дополнительное «ш», чтобы зафиксировать время глобуса. Для 1000 файлов время составляет 0,010 против 0,009. С 10000 это 0,031 против 0,057 (printf медленнее), а список аргументов слишком длинный с * на 100 000 файлов. - person Andrew Dalke; 12.03.2009
comment
Таким образом, хотя в общем случае он работает быстрее (несколько каталогов содержат >2000 файлов), в некоторых случаях он дает сбой, и этот сбой беспокоит меня больше, чем производительность. Кроме того, я только что понял, что ls должен быть «ls -f», поскольку OP не заботился о порядке сортировки. - person Andrew Dalke; 12.03.2009
comment
Возможно, любой, у кого есть работающая система Unix, где 'ls' НЕ кэшируется в виртуальной машине, вероятно, находится в системе, которая ничего не делает. Хотя форк «ls» не будет бесплатным, при кэшировании он наверняка будет чертовски дешевым. - person Will Hartung; 12.03.2009
comment
На самом деле вы не можете получить слишком длинный список аргументов со встроенными командами, поэтому только ls может в конечном итоге сломаться (при условии, что printf является встроенной, а не внешней командой, как уже указывалось). - person Dimitre Radoulov; 12.03.2009
comment
Хотя я согласен с тем, что в большинстве случаев выигрыш в производительности будет незначительным, я не вижу, как ls может быть лучше в этом случае. - person Dimitre Radoulov; 12.03.2009
comment
@Masi,% s - это escape-последовательность символов, которая преобразует аргумент в строку и копирует ее в стандартный вывод. -F7 устанавливает разделитель полей ввода awk FS в строку/цифру 7, NF == 6 означает соответствие только записям, которые имеют ровно четыре поля, NF - количество полей (продолжение следует...) - person Dimitre Radoulov; 13.03.2009
comment
то есть записи, содержащие ровно три вхождения цифры 7. - person Dimitre Radoulov; 13.03.2009
comment
Я на Mac. 'который printf' дает мне /usr/bin/printf как для bash, так и для tcsh. Вот почему я получаю слишком длинный список аргументов. В конце концов, я проверил свои временные показатели. - person Andrew Dalke; 13.03.2009
comment
@dalke, для проверки следует использовать тип printf, а не какой. - person Dimitre Radoulov; 13.03.2009
comment
Интересный. Не знал про тип. Почему «какой cd» сообщает, что cd является встроенной командой, а не printf? Я выполнил свои временные тесты с двумя bash-скриптами: «time x1.sh», «time x2.sh» — это «printf %s\n * › /dev/null» и «ls › /dev/null», и я получил список Arg. слишком долго. Почему? Можете ли вы сделать лучшее время? - person Andrew Dalke; 13.03.2009
comment
Вы получаете сообщение об ошибке слишком длинного списка Arg при превышении предела системной переменной ARG_MAX. Это на машине SunOS 5.8 с bash 2.03.0(1): bash-2.03$ getconf ARG_MAX 1048320 bash-2.03$ printf '%s' * | perl -nle'длина печати' 1083857 (продолжение...) - person Dimitre Radoulov; 13.03.2009
comment
ls завершится ошибкой при передаче аргументов (например, для фильтрации по шаблону): ‹pre›bash-2.03$ time ls * ›&- bash: /usr/bin/ls: список аргументов слишком длинный, реальный пользователь 0m1.103s 0m0.890s sys 0m0.370s bash-2.03$ time printf '%s' * ›&- real 0m1.211s пользователь 0m1.120s sys 0m0.090s ‹/pre› - person Dimitre Radoulov; 13.03.2009
comment
Встроенный printf не подведет. - person Dimitre Radoulov; 13.03.2009
comment
Это без аргументов: bash-2.03$ time ls ›&- real 0m1.581s user 0m1.100s sys 0m0.430s bash-2.03$ time printf '%s' * ›&- real 0m1.267s user 0m1.130s sys 0m0 .130 с - person Dimitre Radoulov; 13.03.2009
comment
Ваше время printf не работает для измерения времени подстановки, потому что подстановка выполняется оболочкой, которая передает argv в time, который перенаправляет argv в printf. Подстановка происходит до начала времени, поэтому результаты несопоставимы. Вот почему мне пришлось сделать сценарий оболочки, чтобы сделать тайминги - person Andrew Dalke; 13.03.2009
comment
Итак: bash-2.03$ time bash -c 'ls ›&-' real 0m1.583s user 0m1.120s sys 0m0.410s bash-2.03$ time bash -c 'printf %s\n * ›&-' real 0m1. 474с пользователь 0м1.210с система 0м0.260с - person Dimitre Radoulov; 13.03.2009
comment
% time bash -c 'printf %s\n * ›&-' :: Ошибка сегментации :: 0,154u 1,833 с 0:08,45 23,4% :: 0+0k 9283+0io 0pf+0w :: % ls -f | туалет -л :: 399714 - person Andrew Dalke; 14.03.2009
comment
Читая ваши сообщения, я понимаю, что, возможно, я не совсем понял, каждая встроенная команда быстрее и эффективнее, чем внешняя команда, потому что нет необходимости запускать вспомогательную оболочку. Я говорил о том, сколько раз вы выполняете команду, а не о крайних случаях с 399714 файлами (продолжение...) - person Dimitre Radoulov; 14.03.2009
comment
Думаю, этот пример иллюстрирует то, о чем я говорил: time bash -c 'for c in {1..1000};do ls ›/dev/null;done' real 0m15.307s user 0m33.299s sys 0m10.276s time bash -c 'для c в {1..1000};do printf '%s\n' * ›/dev/null;done' real 0m0.537s user 0m0.265s sys 0m0.234s - person Dimitre Radoulov; 14.03.2009
comment
Мои тайминги согласны с вами: printf быстрее, чем ls для небольших каталогов. Когда есть ~ 1000 файлов, мои тесты показывают, что ls работает быстрее. Возможно, из-за плохого использования malloc/string? И printf не работает в крайних случаях. Хотя это немного медленнее, я предпочитаю 'ls -1f', так как он не ломается и его легко понять. - person Andrew Dalke; 16.03.2009

Я не понимаю часть о "случайном порядке". Как вы различаете «порядок», когда повторяется один и тот же токен? Отличается ли «a7b7» от «c7d7» порядком семерок?

Во всяком случае, это должно работать:

 ls *7*7*7*

Это просто позволяет оболочке решить проблему, но, возможно, я не понял правильно.

РЕДАКТИРОВАТЬ: вышеприведенное неверно, оно включает случаи с более чем четырьмя семерками, что нежелательно. Предполагая, что это bash и включен расширенный подстановочный знак, это работает:

ls *([^7])7*([^7])7*([^7])7*([^7])

Это читается как «ноль или более символов, которые не являются семерками, за которыми следует семерка, за которой следует ноль или более символов, которые не являются семерками», и так далее. Важно понимать, что звездочка здесь является оператором префикса, работающим с выражением ([^7]), что означает "любой символ, кроме 7".

person unwind    schedule 12.03.2009
comment
Ваша команда не работает. Я имею в виду, что имя файла может содержать любое количество букв и цифр, и вам нужно найти слова из трех семерок. - person Léo Léopold Hertz 준영; 12.03.2009
comment
Ааа, спасибо, это проясняет. Я знал, что мне чего-то не хватало. :) буду редактировать. - person unwind; 12.03.2009

Я предполагаю, что вы хотите найти файлы, содержащие ровно три семерки, но не более. Использование gnu grep с переключателем регулярных выражений extends (-E):


ls | grep -E '^([^7]*7){3}[^7]*$'

Должен сделать трюк.

По сути, это соответствует 3 вхождениям «не 7, за которыми следует 7», затем группе «не 7» по всей строке (^ и $ в начале и конце шаблона соответственно).

person John Montgomery    schedule 12.03.2009
comment
Это работало на моем хосте Linux. Протестировал это, повторив некоторые текстовые строки, и все выглядело нормально ... Возможно, регулярное выражение не понимается версией grep, которую вы используете? - person John Montgomery; 12.03.2009

Perl-решение:

$ ls | perl -ne 'print if (tr/7/7/ == 3)'
3777
4777
5777
6777
7077
7177
7277
7377
7477
7577
7677
...

(У меня есть каталог с 4-значными номерами. 1777 и 2777 не существуют. :-)

person Jon Ericson    schedule 12.03.2009

Или вместо того, чтобы делать это в одном grep, используйте один grep для поиска файлов с 3 или более семерками, а другой - для фильтрации 4 или более семерок.

ls -f | egrep '7.*7.*7' | grep -v '7.*7.*7.*7'

Вы можете переместить часть работы в шарообразную оболочку с помощью более короткого

ls -f *7*7*7* | grep -v '7.*7.*7.*7'

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

«-f» в «ls» предотвращает сортировку результатов «ls». Если в каталоге огромное количество файлов, то время сортировки может быть довольно заметным.

Этот двухэтапный процесс фильтрации, я думаю, более понятен, чем использование шаблонов [^7].

Кроме того, вот решение в виде скрипта Python, поскольку вы просили об этом в качестве опции.

import os
for filename in os.listdir("."):
    if filename.count("7") == 4:
        print filename

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

person Andrew Dalke    schedule 12.03.2009
comment
Верно. Мне нужен был начальный и конечный. Мой тестовый набор для этого случая был слишком ограничен (только «777» и «7777»). Фиксированный. Спасибо! - person Andrew Dalke; 12.03.2009