learnxinyminutes-docs/ru/bash.md
2024-12-08 23:20:53 -07:00

481 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
contributors:
- ["Max Yankov", "https://github.com/golergka"]
- ["Darren Lin", "https://github.com/CogBear"]
- ["Alexandre Medeiros", "http://alemedeiros.sdf.org"]
- ["Denis Arh", "https://github.com/darh"]
- ["akirahirose", "https://twitter.com/akirahirose"]
- ["Anton Strömkvist", "http://lutic.org/"]
- ["Rahil Momin", "https://github.com/iamrahil"]
- ["Gregrory Kielian", "https://github.com/gskielian"]
- ["Etan Reisner", "https://github.com/deryni"]
- ["Jonathan Wang", "https://github.com/Jonathansw"]
- ["Leo Rudberg", "https://github.com/LOZORD"]
- ["Betsy Lorton", "https://github.com/schbetsy"]
- ["John Detter", "https://github.com/jdetter"]
- ["Harry Mumford-Turner", "https://github.com/harrymt"]
- ["Martin Nicholson", "https://github.com/mn113"]
translators:
- ["Andrey Samsonov", "https://github.com/kryzhovnik"]
- ["Andre Polykanine", "https://github.com/Menelion"]
filename: LearnBash-ru.sh
---
Bash — это командная оболочка unix, которая распространялась как оболочка
для операционной системы GNU и используется в качестве оболочки по умолчанию
для Linux и macOS.
Почти все нижеприведённые примеры могут быть частью shell-скриптов
или исполнены напрямую в shell.
[Подробнее.](http://www.gnu.org/software/bash/manual/bashref.html)
```bash
#!/bin/bash
# Первая строка скрипта — это шебанг, который сообщает системе, как исполнять
# этот скрипт: https://ru.wikipedia.org/wiki/Шебанг_(Unix)
# Как вы уже поняли, комментарии начинаются с «#». Шебанг — тоже комментарий.
# Простой пример hello world:
echo Hello world!
# Отдельные команды начинаются с новой строки или разделяются точкой с запятой:
echo 'Это первая строка'; echo 'Это вторая строка'
# => Это первая строка
# => Это вторая строка
# Вот так объявляется переменная:
VARIABLE="Просто строка"
# но не так:
VARIABLE = "Просто строка"
# Bash решит, что VARIABLE - это команда, которую он должен исполнить,
# и выдаст ошибку, потому что не сможет найти её.
# и не так:
VARIABLE= 'Просто строка'
# Тут Bash решит, что 'Просто строка' — это команда, которую он должен
# исполнить, и выдаст ошибку, потому что не сможет найти такой команды
# (здесь 'VARIABLE=' выглядит как присвоение значения переменной,
# но только в контексте исполнения команды 'Просто строка').
# Использование переменой:
echo $VARIABLE # => Просто строка
echo "$VARIABLE" # => Просто строка
echo '$VARIABLE' # => $Variable
# Когда вы используете переменную — присваиваете, экспортируете и т.д. —
# пишите её имя без $. А для получения значения переменной используйте $.
# Заметьте, что ' (одинарные кавычки) не раскрывают переменные в них.
# Раскрытие параметров ${ }:
echo ${Variable} # => Просто строка
# Это простое использование раскрытия параметров
# Раскрытие параметров получает значение переменной.
# Оно «раскрывает», или печатает это значение.
# ̶Значение можно изменить во время раскрытия.
# Ниже приведены другие модификации при раскрытии параметров.
# Замена подстрок в переменных
echo ${Variable/Просто/Это} # => Это строка
# Заменит первое вхождение «Просто» на «Это»
# Взять подстроку из переменной
LENGTH=7
echo ${VARIABLE:0:LENGTH} # => Просто
# Это выражение вернёт только первые 7 символов переменной VARIABLE
echo ${Variable: -5} # => трока
# Вернёт последние 5 символов (обратите внимание на пробел перед «-5»)
# Длина строки
echo ${#Variable} # => 13
# Значение переменной по умолчанию
echo ${FOO:-"ЗначениеПоУмолчаниюЕслиFooПустаИлиНеНайдена"}
# => ЗначениеПоУмолчаниюЕслиFooПустаИлиНеНайдена
# Это сработает при отсутствующем значении (FOO=) и пустой строке (FOO="");
# ноль (FOO=0) вернёт 0.
# Заметьте, что в любом случае это лишь вернёт значение по умолчанию,
# а значение самой переменной FOO не изменится.
# Объявить массив из 6 элементов
array0=(один два три четыре пять шесть)
# Вывести первый элемент
echo $array0 # => "один"
# Вывести первый элемент
echo ${array0[0]} # => "один"
# Вывести все элементы
echo ${array0[@]} # => "один два три четыре пять шесть"
# Вывести число элементов
echo ${#array0[@]} # => "6"
# Вывести число символов в третьем элементе
echo ${#array0[2]} # => "3"
# Вывести 2 элемента, начиная с четвёртого
echo ${array0[@]:3:2} # => "четыре пять"
# Вывести все элементы, каждый на своей строке
for i in "${array0[@]}"; do
echo "$i"
done
# Раскрытие скобок { }
# Используется для создания произвольных строк
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
# Выведет диапазон от начального до конечного значения
# Встроенные переменные:
# В bash есть полезные встроенные переменные, например
echo "Значение, возвращённое последней программой: $?"
echo "Идентификатор процесса скрипта: $$"
echo "Число аргументов, переданных скрипту: $#"
echo "Все аргументы, переданные скрипту: $@"
echo "Аргументы скрипта, распределённые по отдельным переменным: $1 $2..."
# Теперь, когда мы знаем, как выводить и использовать переменные,
# давайте изучим некоторые другие основы Bash!
# Текущая директория доступна по команде `pwd`.
# `pwd` расшифровывается как «print working directory», т.е.
# «напечатать рабочую директорию».
# Мы также можем использовать встроенную переменную `$PWD`.
# Заметьте, следующие выражения эквивалентны:
echo "Я в $(pwd)" # выполняет `pwd` и раскрывает вывод
echo "Я в $PWD" # раскрывает переменную
# Если вы получаете слишком много информации в терминале или из скрипта,
# команда `clear` очистит экран
clear
# Очистить экран можно также с помощью Ctrl+L
# Чтение аргументов с устройства ввода:
echo "Как Вас зовут?"
read NAME # Обратите внимание, что нам не нужно определять новую переменную
echo Привет, $NAME!
# У нас есть обычная структура if:
# наберите 'man test' для получения подробной информации о форматах условия
if [ $NAME != $USER ]
then
echo "Имя не совпадает с именем пользователя"
else
echo "Имя совпадает с именем пользователя"
fi
# Истинно, если значение $Name не совпадает с текущим именем пользователя
# Примечание: если $Name пуста, bash интерпретирует код так:
if [ != $USER ]
# а это ошибочная команда
# поэтому «безопасный» способ использовать пустые переменные в Bash таков:
if [ "$Name" != $USER ] ...
# при этом, когда $Name пуста, bash видит код так:
if [ "" != $USER ] ...
# что работает правильно
# Также есть условное исполнение
echo "Исполнится всегда" || echo "Исполнится, если первая команда завершится ошибкой"
# => Исполнится всегда
echo "Исполнится всегда" && echo "Исполнится, если первая команда выполнится удачно"
# => Исполнится всегда
# => Исполнится, если первая команда выполнится удачно
# Чтобы использовать && и || в выражениях if, нужно несколько пар скобок:
if [ $NAME == "Стив" ] && [ $AGE -eq 15 ]
then
echo "Исполнится, если $NAME равно Стив И $AGE равно 15."
fi
if [ $NAME == "Дания" ] || [ $NAME == "Зак" ]
then
echo "Исполнится, если $NAME равно Дания ИЛИ Зак."
fi
# Есть ещё оператор «=~», который проверяет строку
# на соответствие регулярному выражению:
Email=me@example.com
if [[ "$Email" =~ [a-z]+@[a-z]{2,}\.(com|net|org) ]]
then
echo "адрес корректный!"
fi
# Обратите внимание, что =~ работает только внутри
# двойных квадратных скобок [[ ]],
# которые несколько отличаются от одинарных скобок [ ].
# Для более подробной информации см. http://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs.
# Переопределить команду «ping» как псевдоним для отправки только пяти пакетов
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` по данной директории и всем её поддиректориям
# Результат предыдущей команды может быть направлен на вход следующей.
# Команда grep фильтрует ввод по шаблону.
# Так мы можем просмотреть только *.txt-файлы в текущей директории:
ls -l | grep "\.txt"
# Для вывода файлов в стандартный поток используйте `cat`:
cat file.txt
# С помощью `cat` мы также можем читать файлы:
Contents=$(cat file.txt)
echo "НАЧАЛО ФАЙЛА\n$Contents\nКОНЕЦ ФАЙЛА" # «\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 # Извините, тут были Leet-хакеры...
# Поскольку Bash работает в контексте текущей директории, вам может понадобиться
# запустить команду в другой директории.
# Для изменения местоположения у нас есть `cd`:
cd ~ # Перейти в домашнюю директорию
cd # Также переходит в домашнюю директорию
cd .. # Перейти на уровень вверх
# (например, из /home/username/Downloads в /home/username)
cd /home/username/Documents # перейти в указанную директорию
cd ~/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 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
python hello.py > /dev/null 2>&1 # передать вывод скрипта и ошибок в «чёрную дыру» /dev/null, т.е., без вывода
# Поток ошибок перезапишет файл, если этот файл существует,
# поэтому, если вы хотите дописывать файл, используйте «>>»:
python hello.py >> "output.out" 2>> "error.err"
# Перезаписать output.txt, дописать 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.txt строкой "#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/ # рекурсивное удаление
# Команды могут быть подставлены в строку с помощью $( ):
# следующие команды выводят число файлов и директорий в текущей директории.
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
# Цикл for можно использовать для действий с файлами.
# Запустим команду «cat» для файлов file1 и file2
for VARIABLE in file1 file2
do
cat "$VARIABLE"
done
# ... или выводом из команд
# Запустим cat для вывода из ls.
for OUTPUT in $(ls)
do
cat "$OUTPUT"
done
# Цикл while:
while [ true ]
do
echo "Здесь тело цикла..."
break
done
# => Здесь тело цикла...
# Вы также можете определять функции
# Определение:
function foo ()
{
echo "Аргументы работают так же, как и аргументы скрипта: $@"
echo "И так: $1 $2..."
echo "Это функция"
return 0
}
# Вызовем функцию `foo` с двумя аргументами, arg1 и arg2:
foo arg1 arg2
# => Аргументы работают так же, как и аргументы скрипта: arg1 arg2
# => И так: arg1 arg2...
# => Это функция
# или просто
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
# заменить каждое вхождение «хорошо» на «прекрасно» в файле file.txt
# (поддерживаются регулярные выражения)
sed -i 's/хорошо/прекрасно/g' file.txt
# вывести в 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 "^foo.*bar$" file.txt
# Команда `trap` позволяет выполнить некую команду, когда ваш скрипт
# принимает определённый Posix-сигнал. В следующем примере `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 .
# Читайте man-документацию Bash командой `man`:
apropos bash
man 1 bash
man bash
# Читайте документацию info (? для справки)
apropos info | grep '^info.*('
man info
info info
info 5 info
# Читайте info-документацию Bash:
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
```