#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
Единственным преимуществом такого подхода является то, что переменные всегда заключаются в кавычки, так что никаких неожиданностей с расширением имени файла не будет.