Инструменты пользователя

Инструменты сайта


learning:bash

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

learning:bash [27.06.2022 12:12] – [Дартс] viacheslavlearning:bash [30.07.2024 19:21] (текущий) – внешнее изменение 127.0.0.1
Строка 1: Строка 1:
 +====== Bash ======
 +[[https://leanpub.com/programming-from-scratch/read|Илья Шпигорь - Программирование на Bash с нуля]]\\
 +[[https://devhints.io/bash|Bash scripting cheatsheet]]\\
 +[[https://learnxinyminutes.com/docs/bash/|Learn X in Y minutes: Bash]]\\
 +[[https://mywiki.wooledge.org/BashGuide|BashGuide]]\\
 +[[https://tldp.org/LDP/abs/html|Advanced Bash-Scripting Guide]]\\
  
 +Плагины для VSCode: Remote - SSH (подключиться по ssh на удаленный хост), Bash Run Button (кнопка запуска скрипта из GUI).
 +===== Основное =====
 +==== Комментарии ====
 +<code bash>
 +# Shebang (в начале скрипта)
 +#!/bin/bash
 +
 +# Комментарий
 +: '
 +Комментарий
 +в несколько строк
 +'
 +</code>
 +
 +==== Переменные ====
 +<code bash>
 +# Переменные
 +declare -r pwdfile=/etc/passwd # переменная read-only
 +pwdfile=/etc/abc.txt # переопределение не сработает, т. к. read-only
 +declare -l str="STRing" # string (-l - lowercase)
 +declare -u str="STRing" # STRING (-u - uppercase)
 +# Quoting a variable preserves whitespace
 +hello="A B  C   D"
 +echo $hello   # A B C D
 +echo "$hello" # A B  C   D
 +
 +# Переменные окружения (должна быть в ВЕРХНЕМ_РЕГИСТРЕ)
 +set # вывести все переменные
 +export FLOWER="Tulip" # задать переменную окружения, работает до выхода из системы
 +unset FLOWER # убрать переменную
 +
 +# В рамках скрипта все переменные по умолчанию глобальные - они будут действовать и в функциях, и в местах их вызова.
 +
 +# Подставить значение (здесь: default), если его нет в переменной
 +echo "my variable contains ${MY_VAR:-default}"
 +</code>
 +
 +==== Алиасы ====
 +<code bash>
 +# Алиас
 +alias showlog="tail -f /var/log/messages" # задать алиас, работает до выхода из системы
 +alias showlog # посмотреть команду алиаса showlog
 +
 +# Чтобы алиас или переменная окружения работали всегда для конкретного пользователя, нужно добавить их в ~/.bashrc
 +# Глобальные настройки для всех пользователей - /etc/bashrc (для функций и алиасов) или /etc/profile.d/custom.sh для переменных окружения
 +</code>
 +
 +==== Математические операции ====
 +''%%(( мат. операция ))%%'' - чтобы провести вычисления, нужно выражение заключить в двойные скобки.\\
 +Чтобы выражение занести в переменную, перед двойными скобками нужен знак ''$''.
 +
 +|Степень |''%%$a ** $b%%''  |
 +|Умножение |''$a * $b''  |
 +|Деление |''$a / $b''  |
 +|Деление с остатком (modulo) |''$a % $b''  |
 +|Сложение |''$a + $b''  |
 +|Вычитание |''$a - $b''  |
 +
 +<code bash>
 +a=4
 +b=$((a+2)) # 6
 +((b++)) # 7
 +((b--)) # 6
 +((b*=5)) # 65
 +((b+=5)) # 70
 +((b/=10)) # 7
 +((b-=2)) # 5
 +
 +echo $((10/3)) # 3, т. к. bash работает только с целыми числами
 +echo 10/3 |bc -l # для работы с дробями нужно привлекать bc
 +</code>
 +
 +==== Сравнение ====
 +''%%[[ expression ]]%%'' - в двойных квадратных скобках, выражение должно отделяться от скобок пробелом, возвращает true (1) или false (0).
 +^ ^Для сравнения строк ^Для сравнения чисел ^
 +|Меньше |''$a < $b'' |''$a -lt $b'' |
 +|Больше |''$a > $b'' |''$a -gt $b'' |
 +|Меньше или равно|''$a %%<=%% $b'' |''$a -le $b'' |
 +|Больше или равно |''$a %%>=%% $b'' |''$a -ge $b'' |
 +|Равно |''$a == $b'' |''$a -eq $b'' |
 +|Не равно |''$a != $b'' |''$a -ne $b'' |
 +
 +^  Логические операторы  ^^
 +| И |''%%[[ $a && $b ]]%%''  |
 +| ИЛИ |''%%[[ $a || $b ]]%%''  |
 +| НЕ |''%%[[ ! $a ]]%%''  |
 +| Ноль? |''%%[[ -z $a ]]%%''  |
 +| Не ноль? |''%%[[ -n $a ]]%%''  |
 +
 +==== Строки ====
 +<code bash>
 +a="hello"
 +b="world"
 +c="$a $b" # Объединение строк
 +echo $c
 +hello world
 +echo ${#c} # длина строки
 +11
 +echo ${c:3} # начать вывод с 4-го символа (нумерация начинается с 0)
 +lo world
 +echo ${c:3:5} # 5 символов после начала вывода
 +lo wo
 +echo ${c: -4} # вывод с конца
 +orld
 +echo ${c: -4:3} # 3 из 4-х последних
 +orl
 +fruits="apple banana banana banana banana pear"
 +echo ${fruits/banana/cherry} # замена 1-го вхождения
 +apple cherry banana banana banana pear
 +echo ${fruits//banana/cherry} # замена всех вхождений
 +apple cherry cherry cherry cherry pear
 +echo ${fruits/#apple/cherry} # замена только в самом начале строки (при несовпадении замены не будет)
 +cherry banana banana banana banana pear
 +echo ${fruits/%pear/cherry} # замена только в самом конце строки (при несовпадении замены не будет)
 +apple banana banana banana banana cherry
 +echo ${fruits/ban*/cherry} # шаблоны сравнения тоже работают
 +apple cherry
 +</code>
 +https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
 +==== Heredoc ====
 +Heredoc - when you need to pass a multiline block of text or code to an interactive command, such as tee, cat or sftp\\
 +https://linuxize.com/post/bash-heredoc
 +<code bash>
 +cat << EOF
 +Hello
 +Good bye
 +EOF
 +
 +# <<- убирает знаки табуляции из начала строк
 +ftp -n <<- EOF
 + open mirrors.xmission.com
 + user anonymous nothinghere
 + ascii
 + cd gutenberg
 + get GUTINDEX.00
 + bye
 +EOF
 +</code>
 +
 +==== Цвета ====
 +=== echo -e ===
 +Есть стандартный довольно замысловатый синтаксис.\\
 +Используется ''echo -e'', для начала обозначения цвета ''\e['', дальше идёт ''начертание;цвет текста;цвет фона'' и в конце ''m''.\\
 +Для того, чтобы сбросить настройки в конце текста, пишется ''\e[0m''.
 +<code bash>
 +echo -e "\e[5;97;41mERROR\e[0m"
 +</code>
 +
 +<WRAP group>
 +<WRAP half column>
 +^Color ^Foreground Code ^Background Code ^
 +|Black |30 |40 |
 +|Red |31 |41 |
 +|Green |32 |42 |
 +|Yellow |33 |43 |
 +|Blue |34 |44 |
 +|Magenta |35 |45 |
 +|Cyan |36 |46 |
 +|Light Gray |37 |47 |
 +|Gray |90 |100 |
 +|Light Red |91 |101 |
 +|Light Green |92 |102 |
 +|Light Yellow |93 |103 |
 +|Light Blue |94 |104 |
 +|Light Magenta |95 |105 |
 +|Light Cyan |96 |106 |
 +|White |97 |107 |
 +</WRAP>
 +
 +<WRAP half column>
 +^Code ^Description ^
 +|0 |Reset/Normal |
 +|1 |Bold text |
 +|2 |Faint text (бледный) |
 +|3 |Italics |
 +|4 |Underlined text |
 +</WRAP>
 +</WRAP>
 +
 +=== tput ===
 +Есть вариант использования tput.
 +<code bash>
 +echo -e $(tput setaf 7; tput setab 1; tput blink)"ERROR"$(tput sgr0)
 +
 +# Все варианты
 +for fg_color in {0..7}; do
 +    set_foreground=$(tput setaf $fg_color)
 +    for bg_color in {0..7}; do
 +        set_background=$(tput setab $bg_color)
 +        echo -n $set_background$set_foreground
 +        printf ' F:%s B:%s ' $fg_color $bg_color
 +    done
 +    echo $(tput sgr0)
 +done
 +</code>
 +{{:learning:pasted:20220529-082145.png}}
 +
 +^Value ^Color ^
 +|0 |Black |
 +|1 |Red |
 +|2 |Green |
 +|3 |Yellow |
 +|4 |Blue |
 +|5 |Magenta |
 +|6 |Cyan |
 +|7 |White |
 +|8 |Not used |
 +|9 |Reset to default color |
 +
 +https://linuxcommand.org/lc3_adv_tput.php
 +
 +==== Классы символов ====
 +^POSIX class ^Equivalent to ^Matches ^
 +|''[:alnum:]'' |''[A-Za-z0-9]'' |digits, uppercase and lowercase letters |
 +|''[:alpha:]'' |''[A-Za-z]'' |upper- and lowercase letters |
 +|''[:ascii:]'' |''[\x00-\x7F]'' |ASCII characters |
 +|''[:blank:]'' |''[ \t]'' |space and TAB characters only |
 +|''[:cntrl:]'' |''[\x00-\x1F\x7F]'' |Control characters |
 +|''[:digit:]'' |''[0-9]'' |digits |
 +|''[:graph:]'' |''[^ [:cntrl:]]'' |graphic characters (all characters which have graphic representation) |
 +|''[:lower:]'' |''[a-z]'' |lowercase letters |
 +|''[:print:]'' |''%%[[:graph:] ]%%'' |graphic characters and space |
 +|''[:punct:]'' |''%%[-!"#$%&'()*+,./:;<=>?@[]^_`{|}~]%%'' |all punctuation characters (all graphic characters except letters and digits) |
 +|''[:space:]'' |''[ \t\n\r\f\v]'' |all blank (whitespace) characters, including spaces, tabs, new lines, carriage returns, form feeds, and vertical tabs |
 +|''[:upper:]'' |''[A-Z]'' |uppercase letters |
 +|''[:word:]'' |''[A-Za-z0-9_]'' |word characters |
 +|''[:xdigit:]'' |''[0-9A-Fa-f]'' |hexadecimal digits |
 +
 +Примеры
 +  * ''%%a[[:digit:]]b%%'' matches a0b, a1b, ..., a9b.
 +  * ''%%a[:digit:]b%%'' is invalid, character classes must be enclosed in brackets
 +  * ''%%[[:digit:]abc]%%'' matches any digit, as well as a, b, and c.
 +  * ''%%[abc[:digit:]]%%'' is the same as the previous, matching any digit, as well as a, b, and c
 +  * ''%%[^ABZ[:lower:]]%%'' matches any character except lowercase letters, A, B, and Z.
 +https://github.com/micromatch/posix-character-classes
 +
 +==== Прочее ====
 +
 +=== Посылка данных в другой скрипт ===
 +<code bash>
 +# first.sh
 +MESSAGE="Hello there"
 +export MESSAGE
 +./second.sh
 +# second.sh
 +echo "The message is: $MESSAGE"
 +# Теперь при вызове ./first.sh будет показываться сообщение The message is: Hello there
 +</code>
 +
 +=== Изменение строки запроса консоли ===
 +Для этого можно менять переменные окружения PS
 +<code bash>
 +set |egrep ^PS
 +PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
 +PS2='> '
 +PS4='+ '
 +
 +export PS1='[ \u @ \w ]> '
 +# Есть разные параметры - \d (date), \t (time), \j (jobs), \h (hostname), \w (current dir) и т. д.
 +</code>
 +
 +<code bash>
 +# История (файл -  ~/.bash_history)
 +history -cw # очистить и записать файл
 +
 +# Скрипт - в начале нужно вставлять строку (# необходим в начале строки):
 +#!/bin/bash
 +
 +which cp # выводит полный путь к команде (программе), в данном случае cp
 +whereis cp # выводит пути к бинарнику команды/программы, к исходникам и к мануалам
 +locate kernel # быстрый поиск путей, содержащих слово kernel. Gрограмма locate может быть не установлена (apt install plocate).
 +updatedb # обновление базы locate, чтобы она увидела только что созданные файлы (она обновляется по крону)
 +</code>
 +
 +===== Сочетания клавиш в консоли =====
 +  * Ctrl+a - в начало строки
 +  * Ctrl+e - в конец строки
 +  * Ctrl+k - удалить всё от курсора до конца строки
 +  * Ctrl+u - удалить всё от курсора до начала строки
 +  * Ctrl+l - очистить экран, кроме текущей строки
 +  * Ctrl+w - удалить последнее слово (до разделителя-пробела)
 +  * Ctrl+t - тащит символ, находящийся позади курсора, вперёд по строке
 +  * Ctrl+f - forward, перемещать курсор вперёд по строке (то же, что и ->)
 +  * Ctrl+b - back, перемещать курсор назад по строке (то же, что и <-)
 +  * Ctrl+h - удалить предыдущий символ (как backspace)
 +  * Ctrl+d - удалить следующий символ (как delete)
 +  * Ctrl+p - показать предыдущую команду (то же, что и ↑)
 +
 +===== Потоки (stdin, stdout, stderr) и перенаправления =====
 +stdout - стандартный вывод.
 +<code bash>
 +cat * > file1.txt # Перенаправить stdout (>) в файл
 +echo "text" >> file1.txt # Перенаправить, добавляя (>>) в файл
 +</code>
 +stderr - вывод ошибок.
 +<code bash>
 +ls absentDir > file1.txt # если каталог не существует, file1.txt будет пустым, т. к. ошибка не будет в стандартном выводе
 +ls absentDir 2> file1.txt # Перенаправление потока ошибок (2>)
 +ls absentDir 2>> file1.txt # Перенаправление потока ошибок (2>>), добавляя в файл
 +ls absentDir 2>> /dev/null # Подавление вывода ошибок
 +
 +# Комбинированный вывод - содержимое существующих файлов будет в out.txt, а ошибки - в err.txt
 +cat file1 file2 nofile > out.txt 2> err.txt
 +# И содержимое, и ошибки будут валиться в out.txt
 +cat file1 file2 nofile > out.txt 2>&1
 +</code>
 +Как сделать, чтобы при перенаправлении потока в файл он не затирался
 +<code bash>
 +set -o noclobber # включить опцию в bash
 +echo "line10" > file1 # -bash: file1: cannot overwrite existing file
 +echo "line10" >> file1 # добавление будет работать
 +set +o noclobber # выключить
 +</code>
 +
 +===== Именованный канал (named pipe) =====
 +Помимо обычных каналов, которые существуют только пока выполняется команда, есть и именованные, работающие пока они не будут удалены либо до перезагрузки компа. Именованные каналы работают по принципу FIFO (первый вошёл - первый вышел).
 +
 +Сиздать именованный канал можно командами ''mkfifo'' или ''mknod p''. В Linux именованный канал отображается как файл с типом p и размером 0. Создав такой канал, мы также можем перенаправить в него вывод других команд, но в отличие от неименованного канала, перенаправление вывода не завершится и надо будет выполнить прерывание Ctrl+c:
 +<code bash>
 +mknod metal_pipe p
 +cat file1 > metal_pipe
 +# ctrl+c
 +ls -l metal_pipe
 +prw-rw-----  1  root  root  0  Sep  23  12:51  metal_pipe
 +</code>
 +===== Условие =====
 +<code bash>
 +c=1
 +if [ $c -eq 1 ]
 +then
 +  echo "c equals 1"
 +elif [ $c -lt 1 ]
 +then
 +  echo "c is less than 1"
 +else
 +  echo "c is bigger than 1"
 +fi
 +# Вместо -eq, -gt и -lt и т. д. можно писать ==, >, <, >= и т. д., но тогда нужны двойные круглые скобки:
 +if (( $c <= 1 ))
 +# Оператор "AND". Все строки ниже дают один и тот же эффект.
 +if [ $c -gt 10 ] && [ $c -lt 30 ]
 +if [[ $c -gt 10 && $c -lt 30 ]]
 +if [ $c -gt 10 -a $c -lt 30 ]
 +# Оператор "OR". Все строки ниже дают один и тот же эффект.
 +if [ $c -gt 10 ] || [ $c -lt 30 ]
 +if [[ $c -gt 10 || $c -lt 30 ]]
 +if [ $c -gt 10 -o $c -lt 30 ]
 +# match
 +if [[ $a =~ [0-9]+ ]] # есть ли в строке цифры
 +
 +# case - типа switch в Powershell (https://linuxize.com/post/bash-case-statement/)
 +echo -n "Enter the name of a country: "
 +read COUNTRY
 +echo -n "The official language of $COUNTRY is "
 +case $COUNTRY in
 +  # скобка завершает список паттернов
 +  Lithuania) echo "Lithuanian";; # ;; - завершение условия
 +  Romania | Moldova) echo "Romanian";;
 +  Italy | "San Marino" | Switzerland | "Vatican City") echo "Italian";;
 +  *) echo "unknown";; # значение по умолчанию
 +esac # завершение case
 +
 +# Выбор из пунктов
 +VEGS=('TOMATO' 'CUCUMBER' 'ONION' 'PEASE' 'RADISH' 'DILL')
 +select VEG in ${VEGS[@]}
 +do
 +  case $VEG in
 +  TOMATO | CUCUMBER | ONION | PEASE | RADISH | DILL)
 +    echo "You've selected $VEG";;
 +  *)
 +    echo "Select from 1 to ${#VEGS[@]}";;
 +  esac
 +done
 +</code>
 +
 +===== Циклы =====
 +
 +<code bash>
 +# while
 +number=1
 +while [ $number -le 10 ]
 +do
 +    echo "$number"
 +    ((number++)) # можно ((number+=1))
 +done
 +
 +# until
 +number=1
 +until [ $number -gt 10 ]
 +do
 +    echo "$number"
 +    ((number++))
 +done
 +
 +# for
 +for i in 1 2 3 4 5 # {1..5} или ${array[@]}
 + do
 +    echo $i
 + done
 +# for i in {0..100..5} - c 0 до 100 с шагом 5
 +# for c in {01..75}; do echo $c; done;   # for two digits
 +# for c in {001..075}; do echo $c; done; # for three digits
 +
 +
 +for (( i=0; i<5; i++ )) # (( задать переменную; условие продолжения цикла; действие ))
 + do
 +    echo $i
 + done
 +
 +# for loop для ассоциированного массива
 +declare -A array
 +array["name"]="Vasya"
 +array["title"]="manager"
 +for i in "${!array[@]}"
 +do
 +  echo "$i: ${array[$i]}"
 +done
 +
 +# прерывание цикла
 +for (( i=0; i<=10; i++ ))
 +do
 +  if [ $i -gt 5 ]
 +  then
 +    break # прервать цикл
 +  fi
 +  echo $i
 +done
 +
 +# продолжение цикла
 +for (( i=0; i<=10; i++ ))
 +do
 +   if [[ $i -eq 3 || $i -eq 7 ]]
 +   then
 +     continue # перейти сразу на след. итерацию
 +   fi
 +   echo $i
 +done
 +
 +</code>
 +
 +===== Входные данные =====
 +
 +<code bash>
 +#! /bin/bash
 +echo $0 $1 $2 $3
 +
 +# $0 - имя скрипта
 +bash test.sh letterA letterB letterC
 +test.sh letterA letterB letterC
 +
 +# Вариант входных данных в скрипт через массив
 +args=("$@") #you can also specify the array size here
 +echo ${args[0]} ${args[1]} ${args[2]}
 +
 +args=("$@")
 +echo $@ # вывести все элементы массива
 +echo $# # вывести число элементов в массиве
 +
 +for i in $@; do
 +echo $i
 +done
 +echo "There are $# arguments."
 +
 +./script.sh one two three
 +one
 +two
 +three
 +There are 3 arguments.
 +
 +# Ввод данных пользователем при выполнении скрипта
 +echo "What's your name?"
 +read name
 +echo "Enter your password"
 +read -s pass # выводиться на экран во время ввода не будет
 +read -p "What's your favourite colour? " colour # не переводить строку при вводе
 +
 +echo "Name: $name, Password: $pass, Colour: $colour"
 +
 +# Выбор из пунктов
 +select animal in "cat" "dog" "bird" "fish"
 +do
 +  echo "You selected $animal!"
 +  break
 +done
 +
 +1) cat
 +2) dog
 +3) bird
 +4) fish
 +#? 4
 +You selected fish!
 +
 +# С выходом
 +select option in "cat" "dog" "quit"
 +do
 +  case $option in
 +    cat) echo "Cats meow";;
 +    dog) echo "Dogs bark";;
 +    quit) break;;
 +    *) echo "You've chosen something that I don't know";;
 +  esac
 +done
 +
 +</code>
 +
 +===== Проверки =====
 +<code bash>
 +# Программа работает, только если аргументов достаточно
 +if [ $# -lt 3 ]; then
 +cat <<- EOF
 + Three arguments required:
 + name, id, favourite color.
 + EOF
 +else
 +echo "Name: $1"
 +echo "ID: $2"
 +echo "Color: $3"
 +fi
 +
 +# Повторение вопроса, если нет ответа
 +read -p "Color? " color
 +while [[ -z "$color" ]]; do
 +read -p "Give me an answer! Color? " color
 +done
 +echo "$color was selected."
 +
 +# Ответ по умолчанию, если нет ответа
 +read -p "Color? [green]" color
 +while [[ -z "$color" ]]; do
 +color="green"
 +done
 +echo "$color was selected."
 +
 +# Принимать ответ только если это 4 цифры, если год после 1980 и он не начинается с нуля
 +read -p "Year? [4 digits] " y
 +while [[ ! $y =~ [0-9]{4} ]] || (( $y < 1980 )) || [[ $y =~ ^0 ]]; do
 +read -p "Wrong format or earlier than 1980! Year? " y
 +done
 +echo "Selected year: $y"
 +
 +# Игра "Угадай число"
 +#!/bin/bash
 +function game {
 +s=$((1+ $RANDOM % 10))
 +read -p "Угадайте число от 1 до 10: " n
 +
 +while [[ $n != $s ]]; do
 +  if [[ ! $n ]]; then
 +    read -p "Вы ничего не ввели, напишите число от 1 до 10: " n
 +  elif [[ ! $n =~ ^[0-9]0?$ ]]; then
 +    read -p "Вы ошиблись в написании числа, попробуйте ещё раз: " n
 +  elif [[ $n -lt 1 || $n -gt 10 ]]; then
 +    read -p "Вы ввели число вне диапазона, попробуйте ещё раз: " n
 +  elif [[ $s -gt $n ]]; then
 +    read -p "Загаданное число больше, попытайтесь ещё: " n
 +  elif [[ $s -lt $n ]]; then
 +    read -p "Загаданное число меньше, попытайтесь ещё: " n
 +  fi
 +done
 +
 +echo "Поздравляю, вы угадали, это было число $s!"
 +}
 +
 +if [[ $1 =~ [Gg][Aa][Mm][Ee] ]]; then
 +  game
 +else
 +  echo "Чтобы начать игру, запустите этот скрипт с аргументом 'game'"
 +fi
 +
 +Угадайте число от 1 до 10: ыыы
 +Вы ошиблись в написании числа, попробуйте ещё раз:
 +Вы ничего не ввели, напишите число от 1 до 10: 0
 +Вы ввели число вне диапазона, попробуйте ещё раз: 14
 +Вы ввели число вне диапазона, попробуйте ещё раз: 1
 +Загаданное число больше, попытайтесь ещё: 8
 +Загаданное число меньше, попытайтесь ещё: 6
 +Загаданное число меньше, попытайтесь ещё: 4
 +Загаданное число меньше, попытайтесь ещё: 2
 +Поздравляю, вы угадали, это было число 2!
 +</code>
 +
 +===== Вывод информации =====
 +<code bash>
 +# 1 - standard output, 2 - standard error
 +ls 1> output.txt 2> errors.txt
 +ls 2>&1 # перенаправить ошибки в стандартный вывод
 +</code>
 +
 +===== Строки =====
 +<code bash>
 +if [ "$st1" == "$st2" ] # match/not match
 +if [ "$st1" \ "$st2" ] # st2 smaller/equal
 +c=$st1$st2 # concatenation
 +echo ${st1,,} # to lowercase
 +echo ${st2^^} # to uppercase
 +echo ${st1^} # capitalize first letter
 +</code>
 +
 +===== Числа =====
 +<code bash>
 +n1=256
 +n2=7
 +echo $(( n1 % n2 )) # деление с остатком (так же для +, -, *, /)
 +echo $(expr $n1 + $n2) # вариант через expr (знак умножения надо экранировать - $n1 \* $n2)
 +echo "obase=10; ibase=16; 2042A59C3D70B" | bc # конвертер hex в dec
 +
 +# Арифметическая оценка
 +((var = 12 + 7)) # код возврата 0
 +# Арифметическая оценка
 +var=$((12 + 7)) # var=19
 +
 +# Префиксный и постфиксный инкремент
 +var=1
 +((result = ++var)) # result и var равны 2
 +((result = var++)) # result равен 1, var равен 2
 +
 +# Тернарный оператор
 +# Замена конструкции if-then-else
 +if ((var < 10))
 +then
 +  ((result = 0))
 +else
 +  ((result = var))
 +fi
 +# на (( УСЛОВИЕ ? ДЕЙСТВИЕ 1 : ДЕЙСТВИЕ 2 ))
 +((result = var < 10 ? 0 : var))
 +# в bash тернарный оператор можно использовать только в арифм. оценке или подстановке, команды использовать нельзя
 +</code>
 +===== Массивы =====
 +<code bash>
 +food=('bread' 'butter' 'cheese' 'juice')
 +echo "${food[@]}" # вывести всё
 +bread butter cheese juice
 +echo "${food[2]}" # вывести элемент с индексом 2
 +cheese
 +echo "${food[2+1]}" # вывести элемент с индексом 3
 +juice
 +echo "${food[@]: -1}" # вывести последний элемент
 +juice
 +echo "${food[@]:1:2}" # вывести 2 элемента, начиная с 1
 +butter cheese
 +food[5]="orange" # Добавить элемент в 5-й индекс. 4-й здесь будет пустой
 +food+=("kiwi") # Добавить элемент в конец массива
 +echo "${!food[@]}" # вывести индексы
 +0 1 2 3 5 6
 +echo "${#food[@]}" # вывести кол-во элементов
 +6
 +
 +files=$(ls Documents/*.txt) # Так делать не надо - будет строка
 +declare -a files=(Documents/*.txt) # правильный вариант (индексируемый массив)
 +files=(Documents/*.txt) # Можно и так
 +
 +# Определение элементов массива по отдельности
 +doc="/home/user/doc.txt"
 +files=([0]="$doc" [5]="/usr/share/doc/xz/README") # пропуск элементов (sparse array)
 +
 +# Ассоциированные массивы
 +# Можно задать сразу все элементы
 +declare -A user=(["Name"]="Ivan" ["Surname"]="Petrov" ["Phone"]="+7 000 000-00-00" ["Mail"]="i.petrov@example.com")
 +# Можно задать элементы постепенно
 +declare -A myarray
 +myarray[size]=big
 +myarray[room]="meeting room 1"
 +echo The "${myarray[room]}" is "${myarray[size]}"
 +# Содержимое массива
 +declare -p myarray
 +declare -A myarray=([room]="meeting room 1" [size]="big" )
 +
 +# Список ключей - !
 +echo "${!myarray[@]}"
 +room size
 +
 +# Удалить массив или элемент
 +unset myarray
 +unset myarray[0]
 +unset myarray[size]
 +
 +# Текст в массив
 +mapfile -t names_array < names.txt # синоним команды mapfile - readarray
 +</code>
 +===== Функции =====
 +Рекомендации:
 +  - Имя функции должно сообщать читателю кода, что она делает.
 +  - В функциях нужно использовать только локальные переменные. В ином случае конфликт имён локальных и глобальных переменных решается соглашением об их именовании.
 +  - Не надо использовать глобальные переменные в функциях. Вместо этого значение глобальной переменной передаётся в функцию через параметр.
 +  - Не надо использовать ключевое слово ''function'' при объявлении функций. Оно есть в Bash, но отсутствует в POSIX-стандарте. Использование ''function'' решает проблему между именем функции и псевдонимом, если их имена совпадают. В такой ситуации функцию можно вызвать, поставив перед её именем обратный слэш: ''\name''
 +<code bash>
 +funcName() {
 +    echo $1 $2 # входные позиционные аргументы
 +}
 +funcName "Good morning," "man!"
 +Good morning, man!
 +
 +# Объявление функции в одну строку
 +cpuinfo() { cat /proc/cpuinfo ; } # ; перед } в конце обязательно
 +
 +# Удалить функцию, но не одноимённую переменную (-f)
 +unset -f cpuinfo
 +
 +# Все аргументы, передаваемые в функцию - $@
 +count() {
 +c=1
 +for arg in $@; do
 +  printf "$c:\t$arg\n"
 +  ((c++))
 +done
 +}
 +count $(ls)
 +
 +# Параметры (флаги).
 +# Двоеточие после параметра значит, что нужно указывать значение.
 +# Если двоеточия нет, то проверяется только наличие параметра.
 +while getopts u:p:ab option; do
 +  case $option in
 +    u) user=$OPTARG;;
 +    p) pass=$OPTARG;;
 +    a) echo "Got the A parameter";;
 +    b) echo "Got the B parameter";;
 +    ?) echo "Unknown parameter $OPTARG!";;
 +  esac
 +done
 +
 +echo "User: $user / Pass: $pass"
 +
 +# Область переменных: локальные переменные внутри функций задаются только в пределах этих функций с помощью команды local,
 +# иначе можно, при наличии одинаковых имён в скрипте и внутри функции, получить проблемы, т. к. по умолчанию все переменные - глобальные.
 +suncTest() {
 +i="function variable"
 +}
 +i="script variable"
 +echo $i # script variable
 +suncTest
 +echo $i # function variable
 +
 +suncTest() {
 +local i="function variable"
 +echo $i # function variable,
 +# потому что происходит сокрытие глобальной переменной при объявлении одноимённой локальной.
 +}
 +i="script variable"
 +echo $i # script variable
 +suncTest
 +echo $i # script variable - в скрипте осталась глобальная переменная.
 +
 +# Массивы в функции по умолчанию как раз локальные. Чтобы сделать их глобальными, нужно объявлять их с ключом -g
 +test() {
 +declare -g files=(Documents/*.txt)
 +}
 +</code>
 +
 +===== Файлы, каталоги =====
 +<code bash>
 +mkdir -p /tmp/g/d/f/s/h/r/e/e/directory # создать каталог со всеми вышестоящими
 +touch /tmp/file.txt # создать файл
 +
 +# Проверка существования каталога (файла - ключ -f)
 +if [ -d "$dir" ]
 +then
 +    echo "$dir exists"
 +else
 +    echo "$dir doesn't exist"
 +fi
 +
 +
 +</code>
 +
 +
 +https://linuxhint.com/3hr_bash_tutorial
 +==== Учётки, группы ====
 +<code bash>
 +# Посмотреть, кто заходил в систему
 +last
 +</code>
 +
 +==== Работа с файлами ====
 +<code bash>
 +# Поиск расширений файлов
 +find . -type f -name '*.*' | sed 's|.*\.||' | sort -u
 +</code>
 +
 +===== Примеры =====
 +Если делитель 3,5 или 7, то выводить соответственно Pling, Plang или Plong. Если заданное число-аргумент делится на несколько делителей сразу, Соединять слова. Если заданное число не делится ни на один из делителей, выводить само число. ([[https://en.wikipedia.org/wiki/Fizz_buzz|Fizz buzz]])
 +<code bash>
 +#!/bin/bash
 +
 +array[3]="i"
 +array[5]="a"
 +array[7]="o"
 +
 +main() {
 +for f in "${!array[@]}"; do
 +    if ! (( $1 % $f )); then
 +        str+="Pl${array[$f]}ng"
 +    fi
 +done
 +echo "$str"
 +}
 +
 +result=$(main "$@")
 +
 +if [ -n "$result" ]; then
 +        echo "$result"
 +else
 +        echo "$@"
 +fi
 +</code>
 +
 +==== Сравнение строк и вывод кол-ва различающихся символов ====
 +<code bash>
 +if [ $# -lt 2 ]; then echo "Usage: $0 <string1> <string2>"; exit 1; fi
 +if [ ${#1} -ne ${#2} ]; then echo "strands must be of equal length"; exit 1; fi
 +
 +for (( c=0; c<=${#1}; c++ )); do
 +        if [[ "${1:$c:1}" != "${2:$c:1}" ]]; then
 +                ((d++))
 +        fi
 +done
 +
 +echo "${d:-0}"
 +</code>
 +
 +==== Акроним из строки ====
 +<code bash>
 +# Ненужное echo:
 +# array=$(echo "${@//[^\'[:alpha:]]/ }")
 +array="${@//[^\'[:alpha:]]/ }" # Учтён также апостроф
 +
 +for i in ${array[@]}; do
 +        declare -u str+="${i:0:1}"
 +done
 +
 +echo "$str"
 +</code>
 +
 +==== Проверка числа, является ли оно числом Армстронга ====
 +
 +
 +<code bash>
 +for (( c=0; c<${#1}; c++ )); do
 +        d=$((d + (${1:$c:1} ** ${#1}))) # $d в выражении не нужен
 +done
 +
 +if [[ $d -eq $*  ]]; then
 +        echo "true"
 +else
 +        echo "false"
 +fi
 +</code>
 +[[https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%90%D1%80%D0%BC%D1%81%D1%82%D1%80%D0%BE%D0%BD%D0%B3%D0%B0|Число Армстронга]]\\
 +https://www.shellcheck.net/wiki/SC2004\\
 +https://www.shellcheck.net/wiki/SC2199
 +
 +==== Проверка, является ли предложение панграммой ====
 +<code bash>
 +# Assigning an array to a string! Assign as array, or use * instead of @ to concatenate.
 +declare -l str="$*"
 +
 +for i in {a..z}; do
 +# Remove quotes from right-hand side of =~ to match as a regex rather than literally.
 +    if [[ ! "$str" =~ $i ]]; then
 +        echo "false"
 +        exit
 +    fi
 +done
 +
 +echo "true"
 +</code>
 +
 +==== Ответы на разные обращения ====
 +В конструкции case сверху надо указывать наиболее специфичные конструкции.
 +<code bash>
 +shopt -s extglob
 +
 +str="${*//[^[:alnum:]?]}"
 +
 +case "$str" in
 +*([^[:lower:]])+([[:upper:]])*([^[:lower:]])\?) echo "YELL QUESTION?";;
 +*([^[:lower:]])+([[:upper:]])*([^[:lower:]])) echo "YELL!!!1";;
 +"") echo "Empty string";;
 +*\?) echo "Normal question" ;;
 +*) echo "Something else" ;;
 +esac
 +
 +</code>
 +В case используются не регулярки, а [[https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Pattern-Matching|pattern matching]].\\
 +''shopt -s extglob'' - это включение [[https://www.linuxjournal.com/content/bash-extended-globbing|extended globbing]].
 +
 +==== Вычисление очков по буквам слова ====
 +<code bash>
 +# Quote parameters to tr to prevent glob expansion
 +# Declare and assign separately to avoid masking return values
 +# declare -u str=$(echo "$@" |tr -cd [[:alpha:]])
 +
 +# Don't use [] around ranges in tr, it replaces literal square brackets.
 +str=$(echo "${@^^}" |tr -cd '[:alpha:]')
 +num=0 # Условие - ноль тоже нужно вывести, если ввод пустой, а так можно и без этой переменной
 +
 +for (( c=0; c<=${#str}; c++ )); do
 +    case ${str:$c:1} in
 +        A|E|I|O|U|L|N|R|S|T) num=$((num+1));;
 +        D|G) num=$((num+2));;
 +        B|C|M|P) num=$((num+3));;
 +        F|H|V|W|Y) num=$((num+4));;
 +        K) num=$((num+5));;
 +        J|X) num=$((num+8));;
 +        Q|Z) num=$((num+10));;
 +    esac
 +done
 +
 +echo "$num"
 +</code>
 +https://www.shellcheck.net/wiki/SC2060\\
 +https://www.shellcheck.net/wiki/SC2021
 +
 +==== Подсчёт зёрен на клетках шахматной доски ====
 +''bc'' применяется, потому что bash не может работать с числами больше 2<sup>63</sup>-1.
 +<code bash>
 +squarecalc() {
 +# Argument mixes string and array. Use * or separate argument.
 +bc <<< "2^($@-1)"
 +}
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +if [[ $@ -ge 1 && $@ -le 64 ]]; then
 +        # Double quote array expansions to avoid re-splitting elements.
 +        # Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
 +        echo $(squarecalc "$@")
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +elif [[ $@ == "total" ]]; then
 +        t=0
 +        for ((c=1; c<=64; c++)); do
 +                d=$(squarecalc $c)
 +                t=$(bc <<< "$t+$d")
 +        done
 +        echo "$t"
 +else
 +        echo "Error: invalid input"
 +        exit 1
 +fi
 +</code>
 +https://www.shellcheck.net/wiki/SC2068\\
 +https://www.shellcheck.net/wiki/SC2145\\
 +https://www.shellcheck.net/wiki/SC2199\\
 +https://www.shellcheck.net/wiki/SC2046\\
 +https://www.shellcheck.net/wiki/SC2086\\
 +https://www.shellcheck.net/wiki/SC2005
 +
 +==== Алгоритм Луна (проверка номера кредитки) ====
 +<code bash>
 +check() {
 +# Идём по знакам по порядку
 +for ((c=0;c<${#1};c++)) do
 +num=${1:$c:1}
 +# Если номер знака нечётный (здесь 0 даёт false), то умножаем его на 2,
 +# $/${} is unnecessary on arithmetic variables.
 +if ! (($c % 2)); then
 +        num=$((num*2))
 +        # а если после этого он стал больше 9, вычитаем 9
 +        if [[ $num -gt 9 ]]; then
 +                num=$((num-9))
 +        fi
 +fi
 +# Добавляем цифру в строку с плюсом
 +dbl+="$num+"
 +done
 +# В конце 0, чтобы строка не заканчивалась плюсом
 +echo "${dbl}0"
 +}
 +
 +# Убрать пробелы
 +cn="${*//[[:blank:]]}"
 +# Если знаков меньше 2 и есть что-то кроме цифр - выход
 +# Use -n instead of ! -z.
 +if [[ "${#cn}" -lt 2 || ! -z "${cn//[[:digit:]]}" ]]; then echo "false"; exit; fi
 +
 +# Если знаков нечётное кол-во - добавляем 0 в начало
 +if ((${#cn} % 2)); then
 +        cn="0$cn"
 +        terms=$(check "$cn")
 +else
 +        terms=$(check "$cn")
 +fi
 +
 +# Если сумма знаков кратна 10 (здесь 0 даёт false), то номер правильный
 +# $/${} is unnecessary on arithmetic variables.
 +if ! ((($terms) % 10)); then
 +  echo "true"
 +else
 +  echo "false"
 +fi
 +</code>
 +https://www.shellcheck.net/wiki/SC2004\\
 +
 +==== Шифр Атбаш ====
 +При кодировании (''./script.sh encode word word'') - замещение букв зеркальными по алфавиту, цифры остаются, потом строка разбивается на сегменты по 5 символов.\\
 +При декодировании (''./script.sh decode dliwd liw'') - то же, только сегменты лепятся обратно в сплошную строку (''wordword'').
 +<code bash>
 +declare -a az=( {a..z} )
 +declare -a za=( {z..a} )
 +
 +str="${*:2}"
 +str=$(echo "${str,,}" |tr -cd '[:alnum:]')
 +
 +declare -A array
 +for ((c=0;c<"${#az[@]}";c++)); do
 +  array[${az[$c]}]="${za[$c]}"
 +done
 +for n in {0..9}; do array[$n]=$n; done
 +
 +for ((c=0;c<${#str};c++)); do
 + result+="${array[${str:$c:1}]}"
 +done
 +
 +case $1 in
 +  encode) result=$(echo "$result" |fold -w5) && echo $result;;
 +  decode) echo "$result";;
 +esac
 +
 +</code>
 +
 +==== Строка наоборот ====
 +<code bash>
 +input="$*"
 +
 +for ((c=${#input};c>=0;c--)); do
 +str+="${input:$c:1}"
 +done
 +
 +echo "$str"
 +</code>
 +
 +==== Проверка на високосный год ====
 +Год должен делиться на 4, исключая те, что делятся на 100 (но если этот исключённый делится на 400, то включить).
 +<code bash>
 +# Аргумент должен присутствовать, и в нём не должно быть ничего, кроме цифр
 +if [[ -z $* || -n ${*//[[:digit:]]} ]]; then echo "Usage: $0 <year>"; exit 1; fi
 +
 +if (( $*%4==0 && ($*%100!=0 || $*%400==0) )); then
 +echo "true"
 +else
 +echo "false"
 +fi
 +</code>
 +
 +==== Число резисторов ====
 +<code bash>
 +declare -A array=(
 +[black]=0
 +[brown]=1
 +[red]=2
 +[orange]=3
 +[yellow]=4
 +[green]=5
 +[blue]=6
 +[violet]=7
 +[grey]=8
 +[white]=9
 +)
 +
 +# Непонятно, как сравнивать сразу с 2 значениями
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +if [[ ! ${!array[@]} =~ $1 || ! ${!array[@]} =~ $2 ]]; then
 +  echo "invalid color"
 +  exit 1
 +fi
 +# 1-е число не должно быть нулём
 +echo "${array[$1]//0}${array[$2]}"
 +</code>
 +https://www.shellcheck.net/wiki/SC2199
 +
 +Вариант 2 - резистора три, третий добавляет соответствующее кол-во нулей. От нулей зависит потом - кило- мега- гига- или просто омы.
 +<code bash>
 +shopt -s extglob
 +
 +declare -A colors=(
 +[black]=0
 +[brown]=1
 +[red]=2
 +[orange]=3
 +[yellow]=4
 +[green]=5
 +[blue]=6
 +[violet]=7
 +[grey]=8
 +[white]=9
 +)
 +
 +zero() {
 +for ((c=1;c<=$1;c++)); do
 +  echo -n "0"
 +done
 +}
 +
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +if [[ ! ${!colors[@]} =~ $1 || ! ${!colors[@]} =~ $2 || ! ${!colors[@]} =~ $3 ]]; then
 +  echo "Wrong color"
 +  exit 1
 +fi
 +
 +num="${colors[$1]}${colors[$2]}$(zero "${colors[$3]}")"
 +num="${num##+(0)}"
 +
 +case "$num" in
 +*000000000) echo "${num//000000000} gigaohms" ;;
 +*000000) echo "${num//000000} megaohms" ;;
 +*000) echo "${num//000} kiloohms" ;;
 +*(0)) echo "0 ohms" ;;
 +*) echo "$num ohms" ;;
 +esac
 +</code>
 +==== Последовательность слов ====
 +В соответствии с разрядами двоичного числа. Старший разряд - перевернуть массив.
 +<code bash>
 +bin=({0..1}{0..1}{0..1}{0..1}{0..1})
 +n="${bin[$1]}"
 +
 +if [[ ${n:4:1} -eq 1 ]]; then fw+=("wink"); fi
 +if [[ ${n:3:1} -eq 1 ]]; then fw+=("double blink"); fi
 +if [[ ${n:2:1} -eq 1 ]]; then fw+=("close your eyes"); fi
 +if [[ ${n:1:1} -eq 1 ]]; then fw+=("jump"); fi
 +if [[ ${n:0:1} -eq 1 ]]; then
 +  for i in $(seq 1 ${#fw[@]}); do
 +    rev+=("${fw[-$i]}")
 +  done
 +fi
 +
 +# Разделитель IFS работает только с [*], с [@] - нет.
 +if [[ -n $rev ]]; then
 +IFS=, ; echo "${rev[*]}"
 +else
 +IFS=, ; echo "${fw[*]}"
 +fi
 +</code>
 +
 +==== Дартс ====
 +Радиус -1-1 - 10 очков, -5-5 - 5 очков, -10-10 - 1 очко. Допускаются дроби: -0.4, 5.5 и т. п.\\
 +x<sup>2</sup> + y<sup>2</sup> %%<=%% R<sup>2</sup>\\
 +https://stackoverflow.com/questions/481144/equation-for-testing-if-a-point-is-inside-a-circle
 +<code bash>
 +if [[ $# -ne 2 || -n ${1//[0-9\.-]} || -n ${2//[0-9\.-]} ]]; then
 +  echo "error"
 +  exit 1
 +fi
 +
 +coord() {
 +echo "$1^2 + $2^2 <= $3" |bc
 +}
 +
 +if [[ $(coord $1 $2 1) -eq 1 ]]; then
 +  echo "10"
 +  exit 0
 +elif [[ $(coord $1 $2 25) -eq 1 ]]; then
 +  echo "5"
 +  exit 0
 +elif [[ $(coord $1 $2 100) -eq 1 ]]; then
 +  echo "1"
 +  exit 0
 +else
 + echo "0"
 +fi
 +</code>
 +
 +==== Проверка правильности закрытия кавычек в строке ====
 +Участвуют кавычки ''( ) [ ] { }''.
 +<code bash>
 +declare -A ref=([\)]='(' [\]]='[' [\}]='{')
 +str="${*//[^\[\]\{\}\(\)]}"
 +
 +for ((c=0;c<${#str};c++)); do
 +cur=${str:$c:1}
 +  case $cur in
 +    \{|\(|\[) a+=("$cur") ;;
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 + *) if [[ -z ${a[@]} ]]; then
 +        echo "false"
 +        exit
 +    else
 +        # Quote the right-hand side of != in [[ ]] to prevent glob matching.
 +        if [[ ${ref[$cur]} != ${a[-1]} ]]; then
 +            echo "false"
 +            exit
 +        else
 +            unset 'a[-1]'
 +        fi
 +    fi ;;
 +  esac
 +done
 +
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +if [[ -z ${a[@]} ]]; then
 +echo "true"
 +else
 +echo "false"
 +fi
 +</code>
 +https://www.shellcheck.net/wiki/SC2199\\
 +https://www.shellcheck.net/wiki/SC2053
 +
 +Гораздо более удобный вариант удаления последнего символа - ''a=${a%?}''. Соответственно, можно обойтись без массива ''${a[@]}'', как и без вложенных ''if'' (но у меня почему-то не работало ИЛИ).
 +
 +==== Гвоздь и подкова ====
 +Генератор пословицы из набора слов на входе.
 +<code bash>
 +a=("$@")
 +# Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
 +if [[ -z ${a[@]} ]]; then exit; fi
 +
 +for ((c=0;c<${#a[@]}-1;c++)); do
 +echo "For want of a ${a[$c]} the ${a[$c+1]} was lost."
 +done
 +
 +echo "And all for the want of a ${a[0]}."
 +</code>
 +For want of a nail the shoe was lost.\\ For want of a shoe the horse was lost.\\ For want of a horse the rider was lost.\\ For want of a rider the message was lost.\\ For want of a message the battle was lost.\\ For want of a battle the kingdom was lost.\\ And all for the want of a horseshoe nail.\\
 +[[https://en.wikipedia.org/wiki/For_Want_of_a_Nail|For Want of a Nail]]

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki