Bash, рассчитывающий на переменную

#bash #shell

#bash #оболочка

Вопрос:

У меня есть вызываемая переменная ip , которая содержит список IP-адресов.

 $ echo $ip
218.25.208.92 218.25.208.92 53.170.26.175 
8.135.72.164 244.105.203.71 211.118.89.4 
100.29.91.148 187.225.13.147 48.204.157.1452 182.237.138.26 182.237.138.26 182.237.138.26
211.118.89.4 100.29.91.148 100.29.91.148
 

Я пытаюсь выполнить итерацию по этой строке и найти IP-адреса, которые повторяются более 2 раз. Если IP-адрес повторяется более двух раз, покажите, сколько раз IP-адрес был найден.

Я думал, что этим можно управлять с помощью цикла for, но я совершенно потерян.

Я пытался использовать grep -o . Он показывает, сколько раз появляется этот IP-адрес, но я должен указать каждый IP-адрес вручную…

 echo $ip | grep -o 218.25.208.92 | wc -l 
 

Более новый код:

 for i in $ip; do
    echo $ip | grep -o $i | wc -l
done
 

Комментарии:

1. Используйте uniq команду с параметрами -c и. -d

Ответ №1:

С:

 ip='218.25.208.92 218.25.208.92 53.170.26.175
8.135.72.164 244.105.203.71 211.118.89.4
100.29.91.148 187.225.13.147 48.204.157.1452 182.237.138.26
182.237.138.26 182.237.138.26
211.118.89.4 100.29.91.148 100.29.91.148'
 

Важная часть, которую вы упускаете, — это то, как bash обрабатывает разделяющие слова. Bash предоставляет внутренний разделитель полей, который по умолчанию IFS=$' tn' равен ( space , tab , newline ) . Когда вам нужно получить доступ к отдельным словам, вам просто нужно разрешить разделение слов по умолчанию.

Цитирование предотвращает разделение слов. Поэтому, чтобы разрешить разделение слов по умолчанию, вы должны использовать $ip без кавычек. Нет необходимости создавать отдельную подоболочку, вызывающую дополнительные утилиты Linux, bash предоставляет все, что вам нужно, в виде встроенных модулей.

Чтобы просто перебирать каждый отдельный IP-адрес, вы можете просто перебирать содержимое $ip , обрабатывая его как список, например

 for i in $ip; do
    # make use of $i any way you like
    echo $i
done
 

Вы можете просто создать массив из содержимого $ip , объявив массив и инициализировав его с $ip помощью, например

 array=( $ip )
 

Если вы просто хотите разделить каждый IP-адрес на отдельной строке, вы можете использовать трюк printf, например

 printf "%sn" $ip
 

Где строка printf формата, несмотря на наличие одного спецификатора преобразования строки, исчерпывает все входные данные, выводя каждый IP-адрес в отдельную строку.

Комментарии:

1. Имейте в виду, что отсутствие кавычек $ip означает, что они подлежат глобальному расширению. Если он содержит * , ? , или другие подстановочные знаки, они могут быть расширены. Это безопасно с переменной, которая содержит только IP-адреса, но я бы не рекомендовал это для переменных, содержащих произвольный текст или вводимый пользователем.

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

Ответ №2:

Чтобы подсчитать, в моем случае, сколько раз IP-адрес появляется в строке, выполняется следующая команда:

 (IFS=""; sort <<< "$ip") | uniq -c
 

Комментарии:

1. Лучше printf "%sn" $ip | uniq -c — нет необходимости менять значение по умолчанию IFS . Удачи вам в написании сценариев!

2. @DavidC.Rankin IFS изменен в подоболочке, так что долговременного вреда нет. С $ip другой стороны, слово без кавычек подвержено разделению на слова (хорошо), но также и расширению глобуса (плохо).

3. @JohnKugelman — да, я уловил это, намерение состояло в том, чтобы объяснить, что никакая подоболочка не нужна — я думаю, именно так я должен был поэтапно это сделать, а не просто "Better" .

4. отключите глобулирование с помощью set -f

Ответ №3:

Не делайте этого, используйте uniq , но только bash:

 IFS=$' n' read -r -a ips -d '' <<<"$ip"
declare -A count
for i in "${ips[@]}"; do
    (( count["$i"]   ))
done
for i in "${!count[@]}"; do
    (( count["$i"] > 2 )) amp;amp; printf "%st%dn" "$i" "${count[$i]}"
done
 
 100.29.91.148   3
182.237.138.26  3
 

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