Как суммировать номера совпадений

Я новичок в написании сценариев и нуждаюсь в некоторой помощи. По достоинству оценят ваши ответы.

Я получил это задание, которое заключается в том, чтобы найти сумму всех пятизначных чисел (в диапазоне 10000-99999), содержащих ровно два из следующего набора цифр: {4, 5, 6}. Они могут повторяться в пределах одного и того же числа, и если да, они подсчитывают один раз для каждого события.

Некоторые примеры совпадающих чисел – 42057, 74638 и 89515. У меня есть только этот маленький кусок кода, даже не знаю, помогает ли это.

#! /bin/bash for (( CON1=10000; CON1<=99999; CON1++ )) ; do ## UNKNOWN COMMANDS done 

  • Shell Script для перехода через рекурсивно и chmodding в соответствии с условиями типа файла
  • Можно ли выполнить скрипт, который принимает пользовательский ввод, а затем выполнить другой скрипт
  • Вывод команды при передаче по другой команде
  • простой сценарий цикла для построения подсети ipv6
  • Сравнить строки и обновить два разных файла
  • Поиск / usr / dict / words для поиска слов с определенными свойствами
  • Как я могу пропустить пустые файлы с sed?
  • Что означает «$ {- # * i}"! = "$ -" означает?
  • 3 Solutions collect form web for “Как суммировать номера совпадений”

    Вот один из способов подсчета количества 4, 5 или 6 в вашем номере и с помощью bash выполнить оператор, основанный на том, является ли результат двумя или нет:

     $ con1=1457 $ a=${con1//[^456]/}; [ ${#a} -eq 2 ] && echo Yes Yes 

    Я полагаю, вам нужно сделать это в чистом сценарии Bash, но перевод алгоритма John1024 на awk дает значительное ускорение:

     awk 'BEGIN{k=0;for(i=10000;i<100000;i++){j=i;if(gsub(/[456]/,"",j)==2)k+=i};print k}' 

    Это выполняется менее чем за 1/20 времени, которое берет версия bash; это также немного быстрее, чем версия Python, которая использует встроенный метод str.count() Python.

    Начиная

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

     #! /bin/bash for (( CON1=10000; CON1<=99999; CON1++ )) ; do echo $CON1 done 

    Теперь, когда я запустил его, я буду использовать head -5 чтобы показать только первые 5 строк, которые он выводит.

     $ ./cmd.bash | head -5 10000 10001 10002 10003 10004 

    Хорошо, так что выглядит хорошо, проверьте конец следующим образом:

     $ ./cmd.bash | tail -5 99995 99996 99997 99998 99999 

    Это тоже хорошо. Итак, теперь давайте выясним некоторые способы, с помощью которых мы могли бы подойти к следующему этапу идентификации чисел с двумя цифрами из набора {4,5,6}. Мой первый инстинкт здесь – пойти на grep . Существуют также методы для этого исключительно в Bash, но мне нравится использовать различные инструменты, grep , awk и sed для выполнения этих типов вещей, главным образом потому, что так работает мой ум.

    Подход

    Итак, как мы можем использовать строки grep , содержащие 2 цифры из набора, {4,5,6}? Для этого вы можете использовать стандартную нотацию, написанную так в regex, [456] . Вы также можете указать, сколько цифр вы хотите сопоставить с этим набором. Это написано так:

     [456]{#} 

    Где # – число или диапазон чисел. Если бы мы хотели 3, мы напишем [456]{3} . Если бы мы хотели 2-5 цифр, мы бы написали [456]{2,5} . Если вы хотите 3 или более, [456] {3,} `.

    Поэтому для вашего сценария это [456]{2} . Чтобы использовать регулярное выражение в grep , ваша конкретная версия grep должна поддерживать -E swtich. Обычно это доступно в большинстве стандартных grep .

     $ echo "45123" | grep -E "[456]{2}" 45123 

    Кажется, нужно работать, но если мы дадим ему цифры с 3, мы начнем видеть проблему:

     $ echo "45423" | grep -E "[456]{2}" 45423 

    Это тоже подходит. Это потому, что grep не имеет представления о том, что это цифры в строке. Это глупо. Мы сказали, чтобы сообщить нам, если серия символов в нашей строке из набора и что их 2, и в строке 45423 есть 2 цифры.

    Он также терпит неудачу для этих строк:

     $ echo "41412" | grep -E "[456]{2}" $ 

    Так подходит ли этот метод? Это если мы немного изменим тактику, но нам придется перерисовать регулярное выражение.

    пример

     $ echo -e "41123\n44123\n44423\n41423" | grep -E "[^456]*([456][^456]*){2}" 44123 44423 41423 

    Вышеуказанное представляет 4 типа строк. echo -e "41123\n44123\n44423\n41423" просто печатает 4 числа из нашего диапазона.

     $ echo -e "41123\n44123\n44423\n41423" 41123 44123 44423 41423 

    Как работает это регулярное выражение? Он устанавливает шаблон регулярного выражения, равный нулю или более «не [456]», за которым следует 1 или более [456] или ноль или более символов «не [456]», ища 2 появления последних.

    Итак, теперь мы делаем небольшую сборку в вашем скрипте.

     for (( CON1=10000; CON1<=99999; CON1++ )) ; do if echo $CON1 | grep -q -E "[^456]*([456][^456]*){2}"; then echo $CON1 fi done 

    Используя наш head и tail трюк сверху, мы видим, что он работает:

     $ ./cmd.bash | head -5 10044 10045 10046 10054 10055 $ ./cmd.bash | tail -5 99955 99956 99964 99965 99966 

    Но этот метод оказывается собакой медленной. Проблема в том, что grep . Это дорого, и мы запускаем `grep 1 раз, за ​​итерацию через цикл, так что это ~ 80k раз!

    Чтобы улучшить то, что мы могли бы перенести нашу команду grep за пределы цикла и запустить ее 1 раз, после того, как список был сгенерирован, так же, используя нашу оригинальную версию скрипта, которая просто отразила номера:

     $ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" 

    ПРИМЕЧАНИЕ. Мы можем полностью отказаться от цикла for и использовать инструмент командной строки, затем. Это приведет к той же последовательности чисел, что и seq 10000 99999 .

    Один лайнер?

    Необычным способом сделать это будет использование последовательности чисел из приведенной выше команды, а затем передать ее команде paste которая будет вставлять + между каждым числом, а затем запускать этот вывод в калькулятор командной строки, bc .

     $ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+" 10044+10045+10046+10054+10055+10056+10064+10065+10066+10144+10145+... $ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+" | bc 2409327540 

    Но это совсем другой способ решить эту проблему, поэтому вернемся к циклу for .

    Использование чистого Bash

    Поэтому нам нужен какой-то метод тестирования, если цифра имеет ровно 2 цифры в Bash, но не так дорого, как вызов grep 80k раз. Современные версии Bash включают возможность сопоставления с помощью оператора =~ , который может выполнять аналогичное совпадение с grep . Давайте посмотрим на следующее.

     #!/bin/bash for (( CON1=10000; CON1<=99999; CON1++ )) ; if [[ $CON1 =~ [^456]*([456][^456]*){2} ]]; then echo $CON1 fi done 

    Выполнение этого похоже на то, что мы хотим.

     $ ./cmd1.bash | head -5 10044 10045 10046 10054 10055 $ ./cmd1.bash | tail -5 99955 99956 99964 99965 99966 

    Проверка показывает, что теперь он работает с 41511:

     $ ./cmd1.bash | grep 41511 41511 

    Рекомендации

    • Условное выражение – Bash
    Interesting Posts

    wlan0 Не найдено такого устройства

    Служба xinetd apache2-proxy недоступна

    Правило iptables для доступа к Интернету

    Как исправить эти нарушенные зависимости?

    как syslog не регистрировать особые предупреждения?

    Как отклонить письмо, ложно заявляющее, что оно связано с локальным адресом в Postfix?

    терминал История на один день

    % pre section my.rpm больше не выполняется на RHEL 6.7

    Почти не работает команда – ошибка перемещения: символ __getrlimit, версия GLIBC_PRIVATE не определена в libc.so.6

    Поделитесь aliased папкой на хосте Windows с гостевым Linux с помощью VirtualBox

    Как заблокировать ip-адреса, которые получают доступ к более n-страницам в m-prediod времени?

    Совпадение нескольких шаблонов awk и печать в одной строке

    Установите оболочку по умолчанию как bash для пользователей, не находящихся в / etc / passwd

    Кто может изменять разрешения файла / каталога?

    Выход Tmux медленнее, когда существуют вертикальные расщепления: почему?

    Linux и Unix - лучшая ОС в мире.