Created
May 2, 2023 07:17
-
-
Save avtobys/071d86e77d395fe0b63a4062e62cb071 to your computer and use it in GitHub Desktop.
bash learning in one file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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