====== Средства программирования shell ====== * [[http://lib.ru/unixhelp/unixshell.txt|Интерпретатор командного языка shell]] * [[http://mywiki.wooledge.org/BashFAQ|Часто задаваемые вопросы про bash (eng)]] * [[https://habrahabr.ru/post/335960/|Играючи BASH'им]] * [[https://youtu.be/GxVmukxVUo0|Видео урок]] * [[https://www.tutorialspoint.com/execute_bash_online.php|Execute Bash Shell Online]] * [[https://www.shellcheck.net/|ShellCheck finds bugs in your shell scripts.]] ===== Проверка синтаксиса ===== * [[https://www.shellcheck.net/wiki/]] # apt install shellcheck $ shellcheck webd/webd ===== Переменные окружения ===== * [[Переменные окружения]] ===== Объединение команд ===== ==== Последовательность команд в строке ==== * [[https://ss64.com/bash/read.html|read]] $ echo -n "Enter Name: "; read a; echo Hello $a $ sleep 3; echo -e "\007" ==== Последовательности команд в текстовом файле ==== $ cat ex1.txt echo -n "Enter Name: " read p echo Hello $p $ sh ex1.txt $ . ex1.txt ==== Последовательности команд в выполняемом файле ==== $ cat ex1.sh #!/bin/sh echo -n "Enter Name: " read p echo Hello "$p !!!" $ chmod +x ex1.sh $ ex1.sh $ /home/userX/ex1.sh $ ./ex1.sh ==== Последовательность команд в функции ==== $ mycd () { > cd $1 > PS1="`pwd` $ " > } ===== Примеры простых алгоритмов и вариантов использования ===== * [[Коды завершения#Проверка наличия аргументов в скриптах]] ==== Целочисленный цикл (поиск хостов в подсети) ==== * [[Утилита nmap#Ping диапазона адресов с verbose и debug]] $ cat test_ping.sh #!/bin/sh #test -z $1 && exit 1 #[ "$1" ] || { echo Example: ./test_ping.sh 10.5.11; exit 1; } i=1 while [ $i -lt 254 ] do test $i = 50 && continue ping -c 1 -W 1 $1.$i > /dev/null 2>&1 && echo $1.$i || echo No $i i=$(($i + 1)) done $ cat arp_ping.sh #!/bin/sh test $# -lt 1 && exit 1 i=1 while : do ip=$1.$i ping -c 1 -W 1 $ip >/dev/null ip n | grep "$ip " | grep -q ':..:..:' && echo $ip i=$(($i + 1)) test $i -eq 254 && break done ==== Цикл for - обработка множеств (массовое переименование файлов) ==== for i in *sample; do cp $i ${i%.sample}; done ==== Чтение полей из файла ==== $ cat readifs.sh #!/bin/sh IFS=: while read a b c d e f g do echo "$a $g" done < /etc/passwd #!/bin/sh for i in `cut -d: -f1 /etc/passwd` do echo user $i done ==== Перехват сигналов ==== $ cat trap.sh #!/bin/sh trap "echo got signal 2; date" 2 while : ; do cat < ==== Использование файловых дескрипторов ==== Параллельное чтение двух файлов $ cat report.sh #!/bin/sh exec 3 ==== Передача сообщений в syslog ==== $ cat log_gen.sh while : do logger -t cisco -p local0.info "Message 1" logger -t cisco -p local0.info "Message 2" done $ sh cisco_log_gen.sh ===== Примеры использования скриптов sh в системах загрузки ===== ==== Система загрузки rc.local (устаревший способ) ==== # cat /etc/rc #!/bin/sh /usr/sbin/sshd ==== Система загрузки SysV (Linux) ==== === Запуск сервисов === # cat /etc/init.d/ssh #!/bin/sh if [ "$1" = start ] then echo Starting sshd... /usr/sbin/sshd elif [ "$1" = stop ] then echo Stopping sshd... kill -TERM `cat /var/run/sshd.pid` while [ -e /var/run/sshd.pid ] do sleep 1 done else echo Usage: echo "$0 start|stop" fi # cd /etc/init.d # ln -s ../init.d/ssh /etc/rc2.d/S10ssh # ln -s ../init.d/ssh /etc/rc0.d/K15ssh # ln -s ../init.d/ssh /etc/rc6.d/K25ssh # cat /etc/init.d/rc #!/bin/sh runlevel=$1 for s in /etc/rc${runlevel}.d/[SK]*; do if test $s != ${s##/etc/rc${runlevel}.d/S??} then $s start else $s stop fi done # chmod +x /etc/init.d/rc # /etc/init.d/rc 2 # /etc/init.d/rc 0 ==== Система загрузки rc.NG (NetBSD, FreeBSD) ==== === Запуск сервисов === $ cat /etc/rc.d/sshd #!/bin/sh . /etc/defaults/rc.conf . /etc/rc.conf case "$sshd_enable" in [Yy][Ee][Ss]) if [ "$1" = start ] then echo Starting sshd... /usr/sbin/sshd elif [ "$1" = stop ] then echo Stopping sshd... kill -s TERM `cat /var/run/sshd.pid` while [ -e /var/run/sshd.pid ] do sleep 1 done else echo Usage: echo '/etc/rc.d/sshd start|stop' fi ;; esac $ cat /etc/rc.conf ... sshd_enable=YES ... $ cat /etc/rc #!/bin/sh for i in /etc/rc.d/* do $i $1 done === Настройка сетевых маршрутов === $ cat /etc/rc.conf static_routes="wg1 wg2" route_wg1="10.10.10.0/24 10.10.106.100" route_wg2="10.10.20.0/24 10.10.106.100" $ cat /etc/rc.d/routing #!/bin/sh . /etc/rc.conf for i in ${static_routes} do eval route_arg=\$route_${i} route add $route_arg done === Настройка сетевых интерфейсов === $ cat /etc/rc.conf ... ifconfig_fxp0="inet 10.5.2.17/24" ifconfig_fxp0_alias0="inet 10.5.2.18/32" ifconfig_fxp0_alias1="inet 10.5.2.19/32" ifconfig_fxp0_alias2="inet 192.168.15.1/24" ... $ cat /etc/rc.d/netif #!/bin/sh . /etc/rc.conf for intf in `ifconfig | grep mtu | cut -d: -f1` do eval args=\$ifconfig_$intf if [ -n "$args" ] then ifconfig $intf $args alias=0 while : ; do eval ifconfig_args=\$ifconfig_${intf}_alias${alias} if [ -n "${ifconfig_args}" ] then ifconfig $intf ${ifconfig_args} alias alias=$((${alias} + 1)) else break fi done fi done ===== Дополнительные материалы ===== ==== Расширения BASH ==== $ echo a{1..5}b $ echo a{1,3,5}b ==== CGI на shell ==== * [[http://underpop.online.fr/c/cgi/docs/creating-cgi-programs-with-bash/|Creating CGI Programs with Bash: Introduction]] * [[https://debian-administration.org/article/371/A_web_server_in_a_shell_script|A web server in a shell script]] * [[https://stackoverflow.com/questions/16640054/minimal-web-server-using-netcat|Minimal web server using netcat]] * [[https://funprojects.blog/2021/04/11/a-web-server-in-1-line-of-bash/|A Web Server in 1 Line of Bash Code]] ==== Web сервер на shell ==== * [[Сервис HTTP]] * [[Переменные окружения#Чтение значений переменных окружения]] # cat /usr/local/sbin/webd #!/bin/bash base=/var/www #log=/var/log/webd.log read request ##echo "$request" >> $log # for educational demonstration filename="${request#GET }" filename="${filename% HTTP/*}" test $filename = "/" && filename="/index.html" filename="$base$filename" while : do read -r header ## echo "$header" >> $log # for educational demonstration [ "$header" == $'\r' ] && break; ## [ "$header" == $'' ] && break; # for STDIN/STDOUT educational demonstration done if [ -e "$filename" ] then # echo `date` OK $filename on `hostname` >> $log echo -e "HTTP/1.1 200 OK\r" echo -e "Content-Type: $(/usr/bin/file -bi $filename)\r" echo -e "\r" /bin/cat "$filename" else # echo "$(date)" ERR $filename on "$(hostname)" >> "$log" echo -e "HTTP/1.1 404 Not Found\r" echo -e "Content-Type: text/html;\r" echo -e "\r" echo -e "

File $filename Not Found

" # ip=$(awk '/32 host/ { print f } {f=$2}' /proc/net/fib_trie | sort -u | grep -v 127.0.0.1) # echo -e "Host: $(hostname), IP: $ip, ver 1.1" fi
# chmod +x /usr/local/sbin/webd ==== Ресурсы Web сервера на shell ==== # mkdir /var/www # cat /var/www/index.html

Hello from corpX

# mkdir /var/www/img # wget -O /var/www/img/logo.gif http://val.bmstu.ru/unix/Media/logo.gif * [[Сервис INETD]] ==== Проверка ==== http://server.corpX.un/ ==== Asterisk AGI ==== * [[Сервис Asterisk#Asterisk AGI]] ==== Использование диалоговых окон ==== ansible-pull-gpo# cat start.sh #!/bin/bash on_off() { grep -q "$1" "$2" && echo ON || echo OFF } if [ -z ${BR+x} ]; then echo -e "Variable BR (branch) is not set, specify, for example\nexport BR=master"; exit 1; fi apt update || exit 0 apt install dialog ansible git -y GP_OP_FILE=/usr/local/etc/gpo_options.yml GP_EX_FILE=/usr/local/bin/ansible-pull-gpo.sh TEMP_FILE=$(mktemp -t 'XXXX') touch "$GP_OP_FILE" dialog --title "Configurations, Roles and Programs" --clear --nocancel\ --checklist "Choose Options" 19 56 15 \ CONF_RUS_INT: "Russian Interface" "$(on_off CONF_RUS_INT "$GP_OP_FILE")" \ PROG_THBIRD: "Mail client Thunderbird" "$(on_off PROG_THBIRD "$GP_OP_FILE")" \ ROLE_ZAB_AG: "Zabbix Agent" "$(on_off ROLE_ZAB_AG "$GP_OP_FILE")" \ ROLE_OVPN1_CL: "OpenVPN Client" "$(on_off ROLE_OVPN1_CL "$GP_OP_FILE")" \ 2>"$TEMP_FILE" < "$TEMP_FILE" tr " " "\n" > "$GP_OP_FILE" rm "$TEMP_FILE" echo -e "\nEND:" >> "$GP_OP_FILE" echo "/usr/bin/ansible-pull -U http://server.corpX.un/student/ansible-pull-gpo.git -C $BR -e @$GP_OP_FILE" > "$GP_EX_FILE" chmod +x "$GP_EX_FILE" ##exit 0 echo -e "0 */2 * * * sleep \${RANDOM:0:2}m; $GP_EX_FILE\n@reboot sleep 3m; $GP_EX_FILE" | crontab - "$GP_EX_FILE" #apt upgrade -y #reboot === Использование программы whiptail (Linux) === * [[http://rus-linux.net/MyLDP/consol/whiptail.html|Создание диалоговых боксов в интерактивных скриптах оболочки]] * [[https://unix.stackexchange.com/questions/286103/change-edit-text-of-whiptail-gauge|Change/edit text of whiptail gauge]] === Использование программы dialog (FreeBSD) === [radio:~] $ cat /usr/local/bin/internet.sh #!/bin/sh file=`mktemp -t ''` dialog --title "Radio internet" --clear \ --checklist "Choose inet for rooms" 19 56 15 \ 1 "Class 1" ON \ 2 "Class 2" ON \ 3 "Class 3" ON \ 4 "Class 4" ON \ 5 "Class 5" ON \ 6 "Class 6" ON \ 7 "Class 7" ON \ 8 "Class 8" ON \ 9 "Class 9" ON \ 10 "Class 10" ON \ 11 "Class 11" ON \ 12 "Class 12" ON 2>$file case $? in 0) sed -i '' 's/"//g' $file for i in `cat $file` 13 50 100 150 200 201 254 do echo 10.5.${i}.0/24 done > /usr/local/etc/acl/radio_nets.acl sudo /etc/rc.d/pf reload ;; esac rm $file [radio:~] $ cat /etc/pf.conf table persist file "/usr/local/etc/acl/radio_nets.acl" table persist file "/usr/local/etc/acl/squid_nets.acl" nat on dc0 from to any -> (dc0) rdr proto tcp from to ! port 80 -> 127.0.0.1 port 3128 [radio:~] # grep manager /usr/local/etc/sudoers %manager ALL = NOPASSWD: /etc/rc.d/pf reload ==== Работа с mysql ==== sql="select uuid from accounts where uuid=\"$person_uuid\"" uuid=`mysql mybd -B --skip-column-names -e "$sql"` sql="update accounts set uuid=\"$person_uuid\" where uuid=\"$stage_uuid\"" mysql mybd -B --skip-column-names -e "$sql" ===== Отладка ===== set +x set -n