40

Разбор скрипта для командной строки Linux. Часть 1

Недавно в сообществе GNU/Linux появился пост с программой для Shell-а от PetRiot. В комментариях началось обсуждение целесообразности самой программы, но этот вопрос мне не интересен в полной мере, а вот качество самого кода хотелось бы улучшить. Поэтому я решил проанализировать код скрипта и дать пару советов как можно улучшить и сократить код. Надеюсь данный разбор будет полезен как для автора скрипта, так и для остальных читателей.

Начнем с первой строчки:

#!/bin/sh

Shell(sh) - самый старый интерпретатор командной строки, увы у него отсутствует множество возможностей, поэтому лучше использовать /bin/bash.


Данный код работает и в sh, но в дальнейшем мы будем использовать bash:

#!/bin/bash

Далее идет создание(запись названия файлов в переменные) временных файлов:

f_out=.get_ip_ranges
f_tmp=.ips

О том, что эти файлы временные, нам говорит то, что в конце их удаляют:

rm $f_out
rm $f_tmp

Тут сразу бы хотелось отметить несколько вещей:

1) создание временных файлов не всегда хорошо само по себе

2) названия могут конфликтовать с другими файлами, так мы можем случайно затереть важный файл с таким же названием

3) удаление временных файлов в конце кода. В случае ошибки удаление может не сработать(для данного случая маловероятно, но мы же стремимся к хорошему коду)


Первый пункт слишком спорный, поэтому я его проигнорирую.

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


Для решения второго пункта, я бы посоветовал использовать команду mktemp:

f_out="$(mktemp)"
f_tmp="$(mktemp)"

Теперь временные файлы однозначно уникальные, но нужно обеспечить их удаление в конце работы программы(в конце работы программы != в конце кода программы).


Для решения 3 пункта, используем команду trap:

trap "rm -f $f_out $f_tmp" EXIT

Команда trap запустит посланный ей код сразу после завершения программы, тем самым мы можем быть уверенны что файлы будут удалены. Также мы добавили параметр -f команде rm, который нужен для игнорирования ошибок и убирает вопросы о удалении.

Перед тем как перейти к следующей части, хотелось бы добавить ещё одну важную деталь в скрипт - проверку входных параметров.


Автор забыл упомянуть, что для запуска скрипта нужно обязательно послать один аргумент - название сайта. Если этого не сделать, то работа программы будет не очевидна. Добавим в начало проверку:

if [ -z "$1" ]; then
echo "Error: missing argument" 1>&2 ; exit 1
fi

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

Теперь начало кода выглядит так:

#!/bin/bash


if [ -z "$1" ]; then
echo "Error: missing argument" 1>&2 ; exit 1
fi


f_out="$(mktemp)"
f_tmp="$(mktemp)"
trap "rm -f $f_out $f_tmp" EXIT

Идем дальше:

dig $1 A | grep "^$1" | grep -o -E "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"

dig $1 A | grep "^$1" | grep -o -E "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" > $f_out
dig $1 A | grep "^www.$1" | grep -o -E "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
dig $1 A | grep "^www.$1" | grep -o -E "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" >> $f_out

Тут начинаются любимые всеми регексы. Увы читабильность кода плохая, поэтому попробуем это исправить:

# Octet regex

o_re="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"

# IP regex

ip_re="$o_re\.$o_re\.$o_re\.$o_re"


dig $1 A | grep "^$1" | grep -o -E "$ip_re"

dig $1 A | grep "^$1" | grep -o -E "$ip_re" > $f_out

dig $1 A | grep "^www.$1" | grep -o -E "$ip_re"

dig $1 A | grep "^www.$1" | grep -o -E "$ip_re" >> $f_out

Мы создали переменную o_re где находится повторяющая часть ip_re регекса и саму переменную ip_re.


Теперь код короче, но можно сократить ещё:

# Octet regex

o_re="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
# IP regex
ip_re="$o_re\.$o_re\.$o_re\.$o_re"


dig $1 A | grep "^$1" | grep -o -E "$ip_re" | tee "$f_out"
dig $1 A | grep "^www.$1" | grep -o -E "$ip_re" | tee -a "$f_out"

Используя команду tee мы выписали результат команды в командную строку и одновременно в файл $f_out.

Исправив с учетом этого остальную часть кода и получим:

#!/bin/bash


if [ -z "$1" ]; then

echo "Error: missing argument" 1>&2 ; exit 1

fi


f_out="$(mktemp)"

f_tmp="$(mktemp)"


trap "rm -f $f_out $f_tmp" EXIT


echo "*********************************************"

echo "Get A records from DNS:"

# Octet regex

o_re="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"

# IP regex

ip_re="$o_re\.$o_re\.$o_re\.$o_re"


dig $1 A | grep "^$1" | grep -o -E "$ip_re" | tee "$f_out"

dig $1 A | grep "^www.$1" | grep -o -E "$ip_re" | tee -a "$f_out"


echo "*********************************************"

echo "Get NS records from DNS:"

dig $1 NS | grep "^$1" | awk {'print $NF}' | while read nsserv

do

nsname=${nsserv:0:${#nsserv}-1}

echo "=================================="

echo "NS: $nsname"

dig @$nsname $1 A | grep "^$1" | grep -o -E "$ip_re" | tee -a $f_out

done


echo "*********************************************"

echo "Resolve ip range from whois service:"

sort -h $f_out | uniq > $f_tmp

rm $f_out

cat $f_tmp | while read ip

do

echo "Get ip range for $ip"

whois $ip | grep -E -i "inetnum|route|netrange|cidr" >> $f_out

done


echo "*********************************************"

echo "Result"

echo "*********************************************"

sort $f_out | uniq | while read range

do

echo "${range:16}"

done


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


Спасибо за внимание, надеюсь данный пост окажется для вас полезным.

P.s. прошу прощение за орфографические ошибки, мне бы точно не помешал разбор моего текста с орфографической точки зрения :)

GNU/Linux

1.2K постов15.6K подписчиков

Правила сообщества

Все дистрибутивы хороши.

Будьте людьми.

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества