Linux: программирование в bash (полезные скрипты, получение опций в скриптах, функции, условия, циклы, case, массивы)

exit code
При написании скриптов строго говоря нужно знать один exit code: 0 (success).
 
Остальные это не успех по разным причинам. При скриптинге я обычно использовал exit 1 для обозначения любой ошибки, не ориентируясь на таблицу по ссылке выше или другую (противоречивую) информацию по каким-то якобы нормам.
 
Получение exit code status:
# echo $?
1
Пользовательский ввод

Bash read user input

read -p "Enter var: " var
echo "$varv"
Условия

Сравнение строк.

Нулевая строка.

if [[ $var == "" ]]; then
echo "choto"
fi

Сравнение численных в bash:

https://stackoverflow.com/questions/18668556/comparing-numbers-in-bash
if (( $delay > 900 )); then
echo "lol"
fi

if [[ "$delay" -gt "900" ]]; then
echo "lol"
fi
CASE

Пример из configure netmap (автоселект ОС):

#!/bin/sh

os=$(uname -s)
topdir=$(cd $(dirname $0); pwd)

case $os in
Linux)
$topdir/LINUX/configure "$@";;
FreeBSD)
$topdir/FreeBSD/configure "$@";;
*)
echo "Sorry, $os is not supported"
exit 1;;
esac
Функции
select_res() {
grep $1 $2
}

select_res  $1 $2
МАССИВЫ (DICTIONARY, HASH)
declare -A animals=( ["moo"]="cow" ["woof"]="dog")
echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done
опции в скриптах (GETOPS)
  • Множество примеров тут.

Простой пример с четырьмя опциями – a, b, c, f. Три имеют двоеточие в getops и поэтому должны передаваться с аргументами опции, опция f не имеет аргументов и должна находится последней при запуске утилиты (иначе последующие после f опции игнорируются).

while getopts "fa:b:c:" OPT
do
case $OPT in
a) echo "Found option $OPT with arg $OPTARG";;
b) echo "Found option $OPT with arg $OPTARG";;
c) echo "Found option $OPT with arg $OPTARG";;
f) echo "Found option $OPT without arg";;
esac
done

Usage

# ./sw.sh -a test1 -b test2 -c test3 -f test4  # последний аргумент - f передали с опцией, но только от этого нет толку т.к. это не соответствует настройкам
Found option a with arg test1
Found option b with arg test2
Found option c with arg test3
Found option f without arg

# ./sw.sh -a test1 -b test2 -f test4 -c test3 # последний аргумент -c не передался т.к. перед ним аргумент -f без опций
Found option a with arg test1
Found option b with arg test2
Found option f without arg

 

 

 

СКрипты
скрипт по мониторингу (процесса/изменения файла)

Простейший однострочный скрипт, который позволяет онлайн (апдейт каждую секунду) следить за изменением процесса/файла и проч. По сути аналог утилиты watch.

while true; do clear; ps -x; echo; sleep 1; done
скрипт по поиску строк одного файла в другом файле
  • Файл из которого берем строки для поиска указываем в первой переменной.
  • Файл в котором делаем поиск указываем во второй переменной.

Можно убрать якоря, чтобы успешный результат считался не только по полному match строки.

usage:
./str_existance.sh to_find.txt where_find.txt

code:
#!/bin/bash
cat $1 | while read sw

do

 RESULT=$(grep ^"$sw"$ $2)
 if [[ "$RESULT" == "" ]]; then
   echo $sw";"not_exist
 else
   echo $sw";"exist
 fi

done
скрипт по генерации списка IP

Простой скрипт по генерации большого количества IP адресов на базе стартового адреса и необходимого количества последовательных адресов (в примере 100к):

usage:
generate_range.sh 7.7.7.1 100000

code:
#!/bin/bash ip1=$(echo "$1" | awk -F'.' '{print $1}') ip2=$(echo "$1" | awk -F'.' '{print $2}') ip3=$(echo "$1" | awk -F'.' '{print $3}') ip4=$(echo "$1" | awk -F'.' '{print $4}') limit=$(echo "$2") count=0 for a in $(seq $ip1 255); do for b in $(seq $ip2 255); do for c in $(seq $ip3 255); do for d in $(seq $ip4 255); do if [[ $count == $limit ]]; then exit fi echo "$a.$b.$c.$d" let count++ done done done done
скрипт по генерации списка портов на основе диапазона или последовательности

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

#!/bin/bash
echo "$@" | sed 's/,/ /g' | tr " " "\n" | sed 's/[[:space:]]//g' | grep '[0-9]' | while read port
do
RANGE_DETECTION=$(echo $port | grep "-")
if [[ "$RANGE_DETECTION" == "" ]]; then
 echo $port
else
 port=$(echo $port | sed 's/-/../')
 echo `eval echo {$port}` | tr " " "\n"
fi
done

Usage:

$ port_list_from_range 1 
 1

$ port_list_from_range 10-11,13-14
 10
 11
 13
 14

$ port_list_from_range 10-11,13-14 23,24-28
 10
 11
 13
 14
 23
 24
 25
 26
 27
 28

 

Socket

Так же как и ниже с JSON, безусловно лучше использовать python для работы с сокетами. Но если выхода нет – то bash так же возможно использовать socket для приема и передачи файлов.

Чтобы понять, насколько это костыли, достаточно посмотреть, как закрываются соединения 🙂

Первая команда закрывает входное соединение, а вторая закрывает выходное соединение.
$ exec {file-descriptor}<&-
$ exec {file-descriptor}>&-

Пример скрипта, который отправляет файл на указанный IP PORT с использованием TCP сокета вместе с checksum MD5 этого файла (если точнее – сначала передается MD5, затем сам файл). Другие примеры (HTTP GET, SSH CONN) с использованием файлов устройства /dev/tcp и /dev/udp для передачи и приема файлов по ссылке выше.

- Файловые дескрипторы 0, 1 и 2 зарезервированы для stdin, stdout и stderr соответственно. Таким образом, вы должны указать 3 или выше (в зависимости от того, что не используется) в качестве дескриптора файла.
- «<>» означает, что сокет открыт как для чтения, так и для записи. В зависимости от ваших потребностей, вы можете открыть сокет только для чтения (<) или только для записи (>).

#!/bin/bash
IP=$1
PORT=$2
FILE=$3

md5=`md5sum -b $FILE`
exec 3<>/dev/tcp/$IP/$PORT
echo $md5 >&3
sleep 1
cat $FILE >&3

 

JSON

Парсить json через bash можно с использованием jq, но он не включен в стандартные утилиты, поэтому лучше (я так делаю) использовать python и забыть в данном случае про bash. Есть так же промежуточный вариант (костыль) подключения в pipeline python или ruby.

Leave a Reply