Skip to content

Instantly share code, notes, and snippets.

@avtobys
Created May 2, 2023 07:17
Show Gist options
  • Save avtobys/071d86e77d395fe0b63a4062e62cb071 to your computer and use it in GitHub Desktop.
Save avtobys/071d86e77d395fe0b63a4062e62cb071 to your computer and use it in GitHub Desktop.
bash learning in one file
#!/usr/bin/env bash
# Первая строка скрипта - это шебанг, который сообщает системе, как выполнять
# скрипт: https://en.wikipedia.org/wiki/Shebang_(Unix)
# Как вы уже заметили, комментарии начинаются с #. Шебанг также является комментарием.
# Простой пример Hello world:
echo "Hello world!" # => Hello world!
# Каждая команда начинается с новой строки или после точки с запятой:
echo "Это первая команда"; echo "Это вторая команда"
# => Это первая команда
# => Это вторая команда
# Объявление переменной выглядит так:
variable="Некоторая строка"
# Но не так:
variable = "Некоторая строка" # => возвращает ошибку "variable: команда не найдена"
# Bash решит, что `variable` - это команда, которую он должен выполнить, и выдаст ошибку
# из-за того, что она не может быть найдена.
# И не так:
variable= "Некоторая строка" # => возвращает ошибку: "Некоторая строка: команда не найдена"
# Bash решит, что "Некоторая строка" - это команда, которую он должен выполнить, и выдаст ошибку
# из-за того, что она не может быть найдена. В этом случае часть "variable=" рассматривается
# как присваивание переменной, действительное только для области видимости команды "Некоторая строка".
# Использование переменной:
echo "$variable" # => Некоторая строка
echo '$variable' # => $variable
# Когда вы используете саму переменную — присваиваете ее, экспортируете или еще что-то — вы пишете
# ее имя без $. Если вы хотите использовать значение переменной, вы должны использовать $.
# Обратите внимание, что ' (одинарная кавычка) не будет раскрывать переменные!
# Вы можете написать переменную без окружающих кавычек, но это не рекомендуется.
# Раскрытие параметра ${...}:
echo "${variable}" # => Некоторая строка
# Это простое использование раскрытия параметра, такое как два примера выше.
# Раскрытие параметра получает значение из переменной.
# Оно "раскрывает" или выводит значение.
# Во время раскрытия значение или параметр может быть изменено.
# Ниже приведены другие изменения, которые добавляются к этому раскрытию.
# Замена строк в переменных:
echo "${variable/Некоторая/А}" # => A строка
# Это заменит первое вхождение "Некоторая" на "А".
# Извлечение подстроки из переменной:
length=7
echo "${variable:0:length}" # => Некото
# Вернет только первые 7 символов значения
echo "${variable: -5}" # => строка
# Вернет последние 5 символов (обратите внимание на пробел перед -5).
# Пробел перед минусом здесь обязателен.
# Длина строки:
echo "${#variable}" # => 11
# Косвенное расширение:
other_variable="variable"
echo ${!other_variable} # => Некоторая строка
# Раскроет значение `other_variable`.
# Значение по умолчанию для переменной:
echo "${foo:-"ЗначениеЕслиFooОтсутствуетИлиПусто"}"
# => ЗначениеЕслиFooОтсутствуетИлиПусто
# Работает для null (foo=) и пустой строки (foo=""); ноль (foo=0) возвращает 0.
# Обратите внимание, что это только возвращает значение по умолчанию и не изменяет значение переменной.
# Объявить массив из 6 элементов:
array=(один два три четыре пять шесть)
# Вывести первый элемент:
echo "${array[0]}" # => "один"
# Вывести все элементы:
echo "${array[@]}" # => "один два три четыре пять шесть"
# Вывести количество элементов:
echo "${#array[@]}" # => "6"
# Вывести количество символов в третьем элементе
echo "${#array[2]}" # => "3"
# Вывести 2 элемента, начиная с четвертого:
echo "${array[@]:3:2}" # => "четыре пять"
# Вывести все элементы, каждый из них на новой строке.
for item in "${array[@]}"; do
echo "$item"
done
# Встроенные переменные:
# Есть несколько полезных встроенных переменных, таких как:
echo "Последнее возвращаемое значение программы: $?"
echo "PID скрипта: $$"
echo "Количество аргументов, переданных скрипту: $#"
echo "Все аргументы, переданные скрипту: $@"
echo "Аргументы скрипта, разделенные на разные переменные: $1 $2..."
# Расширение фигурных скобок {...}
# используется для генерации произвольных строк:
echo {1..10} # => 1 2 3 4 5 6 7 8 9 10
echo {a..z} # => a b c d e f g h i j k l m n o p q r s t u v w x y z
# Выводит диапазон от начального значения до конечного значения.
# Обратите внимание, что здесь нельзя использовать переменные:
from=1
to=10
echo {$from..$to} # => {$from..$to}
# Теперь, когда мы знаем, как использовать echo и переменные,
# давайте изучим другие основы Bash!
# Наш текущий каталог доступен через команду `pwd`.
# `pwd` означает "print working directory" (вывести рабочий каталог).
# Мы также можем использовать встроенную переменную `$PWD`.
# Обратите внимание, что следующие эквивалентны:
echo "Я в $(pwd)" # выполняет `pwd` и интерполирует вывод
echo "Я в $PWD" # интерполирует переменную
# Если у вас слишком много вывода на экране терминала или из скрипта, команда
# `clear` очищает экран:
clear
# Ctrl-L также работает для очистки вывода.
# Чтение значения из ввода:
echo "Как Вас зовут?"
read name
# Обратите внимание, что нам не нужно было объявлять новую переменную.
echo "Здравствуйте, $name!"
# У нас есть обычная структура if.
# Условие истинно, если значение $name не равно имени пользователя текущего пользователя:
if [[ "$name" != "$USER" ]]; then
echo "Ваше имя не совпадает с именем пользователя"
else
echo "Ваше имя совпадает с именем пользователя"
fi
# Чтобы использовать && и || с операторами if, вам нужно несколько пар квадратных скобок:
read age
if [[ "$name" == "Steve" ]] && [[ "$age" -eq 15 ]]; then
echo "Это выполнится, если $name - Steve И $age равен 15."
fi
if [[ "$name" == "Данил" ]] || [[ "$name" == "Захар" ]]; then
echo "Это выполнится, если $name - Данил ИЛИ Захар."
fi
# Ниже перечислены другие операторы сравнения для чисел:
# -ne - не равно
# -lt - меньше чем
# -gt - больше чем
# -le - меньше или равно
# -ge - больше или равно
# Также есть оператор `=~`, который проверяет строку на соответствие шаблону Regex:
email=me@example.com
if [[ "$email" =~ [a-z]+@[a-z]{2,}\.(com|net|org) ]]
then
echo "Валидный email!"
fi
# Также есть условное выполнение
echo "Всегда выполняется" || echo "Выполняется только если первая команда не выполнена"
# => Всегда выполняется
echo "Всегда выполняется" && echo "Выполняется только если первая команда выполнена"
# => Всегда выполняется
# => Выполняется только если первая команда выполнена
# Одиночный амперсанд & после команды запускает его в фоновом режиме. Вывод фоновой команды
# выводится на терминал, но он не может читать из ввода.
sleep 30 &
# Список фоновых заданий
jobs # => [1]+ Running sleep 30 &
# Перевести фоновое задание на передний план
fg
# Ctrl-C, чтобы убить процесс, или Ctrl-Z, чтобы приостановить его
# Возобновить фоновый процесс после его приостановки с помощью Ctrl-Z
bg
# Убить задание номер 2
kill %2
# %1, %2 и т. д. также можно использовать для fg и bg
# Переопределить команду `ping` в виде псевдонима для отправки только 5 пакетов
alias ping='ping -c 5'
# Выход из псевдонима и использование команды с этим именем
\ping 192.168.1.1
# Список всех псевдонимов
alias -p
# Выражения обозначаются следующим форматом:
echo $(( 10 + 5 )) # => 15
# В отличие от других языков программирования, bash является оболочкой, поэтому работает в контексте
# текущего каталога. Вы можете перечислить файлы и каталоги в текущем
# каталоге с помощью команды ls:
ls # Перечисляет файлы и подкаталоги, содержащиеся в текущем каталоге
# Эта команда имеет параметры, которые управляют ее выполнением:
ls -l # Перечисляет каждый файл и каталог в отдельной строке
ls -t # Сортирует содержимое каталога по дате последней модификации (по убыванию)
ls -R # Рекурсивно `ls` этот каталог и все его подкаталоги
# Результаты (stdout) предыдущей команды могут быть переданы в качестве входных данных (stdin) следующей команде
# с помощью канала |. Команды, объединенные таким образом, называются "конвейером" и выполняются одновременно.
# Команда `grep` фильтрует ввод с предоставленными шаблонами.
# Вот как мы можем перечислить файлы .txt в текущем каталоге:
ls -l | grep "\.txt"
# Используйте `cat` для вывода файлов на stdout:
cat file.txt
# Мы также можем прочитать файл с помощью `cat`:
Contents=$(cat file.txt)
# "\n" выводит символ новой строки
# "-e" для интерпретации символов новой строки как символов управления
echo -e "НАЧАЛО ФАЙЛА\n$Contents\nКОНЕЦ ФАЙЛА"
# => НАЧАЛО ФАЙЛА
# => [содержимое file.txt]
# => КОНЕЦ ФАЙЛА
# Используйте `cp` для копирования файлов или каталогов из одного места в другое.
# `cp` создает НОВЫЕ версии источников,
# поэтому редактирование копии не повлияет на оригинал (и наоборот).
# Обратите внимание, что это перезапишет назначение, если оно уже существует.
cp srcFile.txt clone.txt
cp -r srcDirectory/ dst/ # рекурсивное копирование
# Используйте `scp` или `sftp`, если вы планируете обмениваться файлами между компьютерами.
# `scp` работает очень похоже на `cp`.
# `sftp` более интерактивен.
# Используйте `mv` для перемещения файлов или каталогов из одного места в другое.
# `mv` похож на `cp`, но удаляет исходный файл.
# `mv` также полезен для переименования файлов!
mv s0urc3.txt dst.txt # извините, хакеры l33t...
# Поскольку bash работает в контексте текущего каталога, вы можете захотеть
# выполнить свою команду в другом каталоге. У нас есть cd для смены местоположения:
cd ~ # перейти в домашний каталог
cd # также переходит в домашний каталог
cd .. # перейти на один каталог вверх
# (^^скажем, из /home/username/Downloads в /home/username)
cd /home/username/Documents # перейти в указанный каталог
cd ~/Documents/.. # теперь в домашнем каталоге (если существует ~/Documents)
cd - # вернуться в последний каталог
# => /home/username/Documents
# Используйте подоболочки для работы с каталогами
(echo "Сначала я здесь: $PWD") && (cd someDir; echo "Затем я здесь: $PWD")
pwd # все еще в первом каталоге
# Используйте `mkdir` для создания новых каталогов.
mkdir myNewDir
# Флаг `-p` заставляет создавать новые промежуточные каталоги при необходимости.
mkdir -p myNewDir/with/intermediate/directories
# если промежуточные каталоги не существовали, выполнение указанной
# команды без флага `-p` вызвало бы ошибку
# Вы можете перенаправлять ввод и вывод команд (stdin, stdout и stderr)
# с использованием "операторов перенаправления". В отличие от канала, который передает вывод в команду,
# оператор перенаправления заставляет ввод команды идти из файла или потока, или
# отправляет ее вывод в файл или поток.
# Читать из stdin до ^EOF$ и перезаписать hello.py строками
# между "EOF" (которые называются "здесь документ"):
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# Переменные будут расширены, если первый "EOF" не будет заключен в кавычки
# Запустите скрипт hello.py на Python с различными перенаправлениями stdin, stdout и
# stderr:
python hello.py < "input.in" # передать input.in как ввод для скрипта
python hello.py > "output.out" # перенаправить вывод скрипта в output.out
python hello.py 2> "error.err" # перенаправить вывод ошибок в error.err
python hello.py > "output-and-error.log" 2>&1
# перенаправить и вывод, и ошибки в output-and-error.log
# &1 означает дескриптор файла 1 (stdout), поэтому 2>&1 перенаправляет stderr (2) в текущий
# пункт назначения stdout (1), который был перенаправлен в output-and-error.log.
python hello.py > /dev/null 2>&1
# перенаправить весь вывод и ошибки в черную дыру, /dev/null, то есть без вывода
# Вывод ошибки будет перезаписывать файл, если он существует,
# если вы хотите добавить вместо этого, используйте ">>":
python hello.py >> "output.out" 2>> "error.err"
# Перезаписать output.out, добавить в error.err и подсчитать строки:
info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err
wc -l output.out error.err
# Выполнить команду и распечатать ее дескриптор файла (например, /dev/fd/123)
# см .: man fd
echo <(echo "#helloworld")
# Перезаписать output.out с "#helloworld":
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# Очистить временные файлы подробно (добавьте '-i' для интерактивного)
# ВНИМАНИЕ: команды `rm` не могут быть отменены
rm -v output.out error.err output-and-error.log
rm -r tempDir/ # рекурсивно удалить
# Вы можете установить пакет `trash-cli` для Python, чтобы иметь `trash`,
# который помещает файлы в системную корзину и не удаляет их напрямую
# см. https://pypi.org/project/trash-cli/, если вы хотите быть осторожным
# Команды могут быть заменены внутри других команд с использованием $( ):
# Следующая команда отображает количество файлов и каталогов в
# текущий каталог.
echo "Здесь есть $(ls | wc -l) элементов."
# То же самое можно сделать с помощью косых кавычек ```, но их нельзя вкладывать -
# предпочтительный способ - использовать $( ).
echo "Здесь есть `ls | wc -l` элементов."
# Bash использует оператор `case`, который работает аналогично switch в Java и C++:
case "$Variable" in
# Перечислите шаблоны для условий, которые вы хотите выполнить
0) echo "Есть ноль.";;
1) echo "Есть единица.";;
*) echo "Это не нуль.";; # совпадение со всем
esac
# Циклы `for` выполняются для каждого аргумента:
# Содержимое $Variable выводится три раза.
for Variable in {1..3}
do
echo "$Variable"
done
# => 1
# => 2
# => 3
# Или напишите его в виде "традиционного цикла for":
for ((a=1; a <= 3; a++))
do
echo $a
done
# => 1
# => 2
# => 3
# Они также могут использоваться для действия с файлами ...
# Это выполнит команду `cat` на file1 и file2
for Variable in file1 file2
do
cat "$Variable"
done
# ..или вывод команды
# Это выполнит `cat` для вывода команды `ls`.
for Output in $(ls)
do
cat "$Output"
done
# Bash также может принимать шаблоны, например, для `cat`
# всех файлов Markdown в текущем каталоге
for Output in ./*.markdown
do
cat "$Output"
done
# Цикл while:
while [ true ]
do
echo "тело цикла здесь..."
break
done
# => тело цикла здесь...
# Вы также можете определить функции
# Определение:
function foo ()
{
echo "Аргументы работают так же, как аргументы сценария: $@"
echo "И: $1 $2..."
echo "Это функция"
returnValue=0 # Значения переменных могут быть возвращены
return $returnValue
}
# Вызов функции `foo` с двумя аргументами, arg1 и arg2:
foo arg1 arg2
# => Аргументы работают так же, как аргументы сценария: arg1 arg2
# => И: arg1 arg2...
# => Это функция
# Возвращаемые значения можно получить с помощью $?
resultValue=$?
# Более 9 аргументов также возможны с использованием фигурных скобок, например, ${10}, ${11}, ...
# или просто
bar ()
{
echo "Еще один способ объявления функций!"
return 0
}
# Вызов функции `bar` без аргументов:
bar # => Еще один способ объявления функций!
# Вызов вашей функции
foo "Меня зовут" $Name
# Есть множество полезных команд, которые стоит изучить:
# выводит последние 10 строк файла file.txt
tail -n 10 file.txt
# выводит первые 10 строк файла file.txt
head -n 10 file.txt
# выводит строки файла file.txt в отсортированном порядке
sort file.txt
# выводит или пропускает повторяющиеся строки, с параметром -d выводит их
uniq -d file.txt
# выводит только первую колонку до символа ','
cut -d ',' -f 1 file.txt
# заменяет каждое вхождение 'okay' на 'great' в file.txt
# (совместимо с регулярными выражениями)
sed -i 's/okay/great/g' file.txt
# обратите внимание, что флаг -i означает, что file.txt будет изменен
# -i или --in-place стирает входной файл (используйте --in-place=.backup чтобы сохранить резервную копию)
# выводит на stdout все строки файла file.txt, соответствующие некоторому регулярному выражению
# Пример выводит строки, которые начинаются с "foo" и заканчиваются на "bar"
grep "^foo.*bar$" file.txt
# передайте параметр "-c" вместо этого, чтобы вывести количество строк, соответствующих регулярному выражению
grep -c "^foo.*bar$" file.txt
# Другие полезные параметры:
grep -r "^foo.*bar$" someDir/ # рекурсивно выполняет `grep`
grep -n "^foo.*bar$" file.txt # выводит номера строк
grep -rI "^foo.*bar$" someDir/ # рекурсивно выполняет `grep`, но игнорирует двоичные файлы
# выполните первоначальный поиск, но отфильтруйте строки, содержащие "baz"
grep "^foo.*bar$" file.txt | grep -v "baz"
# если вы буквально хотите искать строку,
# а не регулярное выражение, используйте `fgrep` (или `grep -F`)
fgrep "foobar" file.txt
# Команда `trap` позволяет выполнить команду, когда ваш сценарий
# получает сигнал. Здесь `trap` выполнит `rm`, если он получит любой из
# трех перечисленных сигналов.
trap "rm $TEMP_FILE; exit" SIGHUP SIGINT SIGTERM
# `sudo` используется для выполнения команд от имени суперпользователя
# обычно он интерактивно запрашивает пароль суперпользователя
NAME1=$(whoami)
NAME2=$(sudo whoami)
echo "Был $NAME1, затем стал мощнее $NAME2"
# Читайте документацию о встроенных командах Bash с помощью встроенной команды `help`:
help
help help
help for
help return
help source
help .
# Читайте документацию по страницам справочника Bash с помощью `man`
apropos bash
man 1 bash
man bash
# Читайте документацию по информации с помощью `info` (`?` для справки)
apropos info | grep '^info.*('
man info
info info
info 5 info
# Читайте документацию по информации bash:
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment