summaryrefslogtreecommitdiffhomepage
path: root/ru-ru/bash-ru.html.markdown
blob: ce9183409dd54cffc178917987273fffb4c46cde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
---
category: tool
tool: bash
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
lang: ru-ru
---

Bash — это командная оболочка unix, которая распространялась как оболочка
для операционной системы GNU и используется в качестве оболочки по умолчанию
для Linux и Mac OS X.
Почти все нижеприведённые примеры могут быть частью 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
```