В этом параграфе мы научимся соединять команды между собой и строить из них целые цепочки действий.
Вы узнаете, как данные могут передаваться напрямую от одной команды к другой при помощи пайпов (|) и как управлять порядком и условиями выполнения команд с помощью логических операторов (;, &&, ||).
Ключевые вопросы параграфа
- Что такое пайп (
|) и как он передаёт вывод одной команды в другую? - Как строить простые и сложные цепочки команд?
- Что означает принцип KISS и как он связан с философией Unix?
- Что такое логические операторы и как они управляют последовательностью выполнения?
- Как объединять пайпы и логические операторы?
- Что такое скрипты и зачем они нужны?
Пайпы и конвейеры
В предыдущем параграфе мы познакомились с потоками stdin, stdout и stderr: каждая команда в Linux получает данные на ввод и отправляет результат на вывод.
Иногда одной команды в Linux недостаточно. Вы нашли ошибки в логах, но хотите посчитать, сколько их. Нашли файлы, но нужно отфильтровать только те, что изменялись недавно. Или собрать несколько шагов в одну цепочку.
Вместо того чтобы делать это вручную, Linux предлагает другой путь — соединять команды между собой, чтобы результат одной сразу стал входом для другой.
Так работает пайп (|), который встречали ранее, но теперь мы познакомимся с ним ближе.
Представим, что мы ищем ошибки в логе приложения app.log. Мы уже знаем про команду grep и делаем так:
1alice@ubuntu:~$ grep "ERROR" app.log
Но лог огромный — на сотни строк. Нам нужно понять, какие ошибки встречаются чаще всего. Здесь нам и поможет пайп.
Что делает пайп (|)
Пайп соединяет две команды так, чтобы вывод первой стал вводом второй.
Всё, что команда «говорит» в стандартный поток (stdout), передаётся следующей через «трубу».

Пример:
1alice@ubuntu:~$ ls /etc | less
ls /etcпечатает длинный список файлов;lessумеет показывать текст постранично;|соединяет их: теперьlsпередаёт данные прямо вless.
Без пайпа пришлось бы сохранять вывод во временный файл:
1ls /etc > list.txt
2less list.txt
А с пайпом всё происходит на лету.
Конвейеры из нескольких команд
Когда несколько команд соединены пайпами подряд, это называется конвейер (англ. pipeline).
Каждая команда получает данные от предыдущей и отдаёт следующей:

Каждая из них получает данные от предыдущей и передаёт следующей — как на настоящем заводском конвейере, где каждый «этап» делает свою часть работы.
В результате из простых инструментов можно собрать мощную последовательность действий.
Пример:
1alice@ubuntu:~$ grep "ERROR" app.log | sort | uniq -c | sort -nr
Что здесь происходит:
grep "ERROR" app.log— ищет все строки с ошибками.sort— сортирует эти строки.uniq -c— подсчитывает, сколько раз встречается каждая.sort -nr— сортирует по числу повторений (-n— числовая сортировка,-r— в обратном порядке, то есть от большего к меньшему).
Результат:
1 57 ERROR: database timeout
2 12 ERROR: invalid token
3 3 ERROR: permission denied
Мы получили мини-отчёт об ошибках без единого скрипта. Каждый инструмент делает одно простое действие, но вместе — решают задачу.
Нюансы
Команда uniq умеет находить одинаковые строки, а опция -c позволяет вывести число таких строк. Но для того, чтобы это работало, нужно сначала отсортировать список строк при помощи команды sort.
Команда sort умеет сортировать строки, которые получает на вход. Помимо опций -n (числовая сортировка) и -r (сортировка в обратном порядке), часто используется опция -k. Она позволяет указывать колонку, в выводе по которой мы хотим делать сортировку. Например, -k2 сортирует вывод по второй колонке.
Давайте посмотрим ещё один пример — посчитаем количество повторов в файле с фруктами:
1alice@ubuntu:~$ cat fruits.log
2apple
3apple
4banana
5apple
6cherry
7banana
8cherry
9melon
Мы хотим избавиться от повторений в этом файле. Попробуем использовать uniq:
1alice@ubuntu:~$ cat fruits.log | uniq
2apple
3banana
4apple
5cherry
6banana
7cherry
8melon
Кажется, мало что изменилось, и мы всё ещё видим повторения. Но давайте попробуем сначала отсортировать список:
1alice@ubuntu:~$ cat fruits.log | sort
2apple
3apple
4apple
5banana
6banana
7cherry
8cherry
9melon
Уже лучше! Теперь можем попробовать uniq:
1alice@ubuntu:~$ cat fruits.log | sort | uniq
2apple
3banana
4cherry
5melon
Идеально! Но при помощи опции -c мы сможем увидеть количество повторений для каждого из слов:
1alice@ubuntu:~$ cat fruits.log | sort | uniq -c
2 3 apple
3 2 banana
4 2 cherry
5 1 melon
Так работают sort и uniq, и они очень полезны при работе с файлами и данными.
Давайте попробуем отсортировать список отчётов по их размеру через команду sort:
1alice@ubuntu:~/Projects/2025-10-27$ ls -lh
2total 408K
3-rw-rw-r-- 1 alice alice 1.7K Oct 28 05:14 report1.pdf
4-rw-rw-r-- 1 alice alice 50K Oct 28 05:14 report2.pdf
5-rw-rw-r-- 1 alice alice 35K Oct 28 05:15 report3.pdf
6-rw-rw-r-- 1 alice alice 33K Oct 28 05:15 report4.pdf
7-rw-rw-r-- 1 alice alice 49K Oct 28 05:15 report5.pdf
8-rw-rw-r-- 1 alice alice 97K Oct 28 05:15 report6.pdf
9-rw-rw-r-- 1 alice alice 82K Oct 28 05:16 report7.pdf
10-rw-rw-r-- 1 alice alice 41K Oct 28 05:16 report8.pdf
Интересующая нас колонка находится на пятом месте, она содержит размеры файлов (1.7K, 50K,35K и так далее). Давайте используем опцию -k для сортировки по ней:
1alice@ubuntu:~/Projects/2025-10-27$ ls -lh | sort -k5
2total 408K
3-rw-rw-r-- 1 alice alice 33K Oct 28 05:15 report4.pdf
4-rw-rw-r-- 1 alice alice 35K Oct 28 05:15 report3.pdf
5-rw-rw-r-- 1 alice alice 41K Oct 28 05:16 report8.pdf
6-rw-rw-r-- 1 alice alice 49K Oct 28 05:15 report5.pdf
7-rw-rw-r-- 1 alice alice 50K Oct 28 05:14 report2.pdf
8-rw-rw-r-- 1 alice alice 82K Oct 28 05:16 report7.pdf
9-rw-rw-r-- 1 alice alice 97K Oct 28 05:15 report6.pdf
10-rw-rw-r-- 1 alice alice 1.7K Oct 28 05:14 report1.pdf
Мы отсортировали вывод по конкретной колонке (размеру файлов)!
На самом деле отсортировать файлы по их размеру можно ещё быстрее: у команды ls есть опция -s для этого.
Команда sort — это универсальный инструмент для сортировки строк текста.
Она работает со строками текста, а не с бинарными файлами или реальными базами данных.
Зато в сочетании с другими командами (uniq, grep, wc, head, tail) она становится мощным инструментом анализа текстовых данных, журналов и отчётов.
Когда задачи становятся сложнее, в конвейерах часто используют и другие утилиты, например awk (для анализа данных построчно) или sed (для замены текста «на лету»).
Это очень мощные инструменты, к изучению которых стоит подходить отдельно, — этой главы нам не хватит.
Но их можно использовать следующим образом:
1grep "ERROR" app.log | sed 's/ERROR/WARNING/g' | awk '{print $1}'
Здесь sed заменяет слово ERROR на WARNING, а awk выводит только первый столбец каждой такой строки.
Такие инструменты превращают пайпы в настоящий язык обработки данных.
Создавая пайпы и конвейеры, мы на деле используем один из главных принципов философии Unix — KISS (Keep It Simple, Stupid — «делай проще»). Согласно ему, каждая программа должна делать одно дело, но делать его хорошо.
Вместо того чтобы писать одну огромную утилиту для всех задач, Unix предлагает строить решения из маленьких независимых инструментов, соединяя их между собой пайпами.
Практические примеры
Пайпы используются постоянно, они значительно упрощают работу с данными и выводом команд.
Например, мы можем просмотреть длинный список постранично:
1ls /etc | less
Быстро найти нужную строку в логе:
1cat app.log | grep "error"
Подсчитать количество символов или строк файле:
1cat file.txt | wc -l
Найти определения функций (они обычно начинаются с def) во всех Python-файлах:
1find . -name "*.py" | xargs grep "def"
Без пайпа пришлось бы сначала сохранить вывод в файл и лишь потом работать с этим файлом через less, wc или grep. Пайп делает то же самое мгновенно и без промежуточных файлов.
Каждый пайп (|) создаёт соединение между двумя потоками: выход одной команды — вход следующей.
Так можно передавать текст, списки файлов, логи и даже двоичные данные — всё, что угодно.
Обратите внимание на команду xargs: она очень часто используется совместно с пайпами.
Что делает xargs
Иногда одна команда не умеет читать данные из stdin. Например, grep ищет текст только в файлах, переданных аргументами.
Команда find же выводит список файлов в поток, а не как готовый список аргументов.
Чтобы связать их, как раз используется xargs: он берёт строки из stdin и превращает их в аргументы.
1alice@ubuntu:~$ find . -name "*.py" | xargs grep "def "
findищет все.py-файлы и передаёт их список по пайпу;xargsчитает этот список изstdinи подставляет как аргументы вgrep;grepищет все определения функцийdefво всех найденных файлах.
Это равносильно такой записи:
1grep "def " file1.py file2.py file3.py ...
xargs — универсальный мост между командами, которые не умеют читать поток напрямую.
Как работает пайп
Команды, соединённые пайпом, работают одновременно. Bash запускает их как отдельные процессы и соединяет каналом в памяти.
Пока первая команда пишет данные, вторая уже читает их. Но есть важная деталь: не каждая команда умеет обрабатывать поток «на лету».
Например, если мы соединим grep с head:
1grep "ERROR" app.log | head -n 5
— результат появится почти мгновенно.
head берёт первые пять строк и завершает работу, а grep автоматически останавливается, потому что пайп закрылся.
Так выглядит настоящий потоковый конвейер: данные текут построчно, без задержек.
Но если заменить head на sort:
1grep "ERROR" app.log | sort
— результат появится только после того, как grep закончит.
Это происходит потому, что sort должен сначала получить весь поток данных целиком, чтобы понять, в каком порядке их отсортировать.
Пока поток не закончится — sort ждёт, собирая всё в память или во временный файл.
Похожим образом ведёт себя и tail: он не может показать последние пять строк, пока не узнает, где конец.
Зато tail -f («следить за потоком») работает как раз наоборот: показывает строки по мере поступления, в реальном времени.
Можно сказать, что команды в пайпе действительно работают параллельно, но когда вы увидите результат — зависит от того, потоковая это команда или нет.
head,grep,wcиawkпоказывают результат сразу,sortиtailждут, пока поток завершится.
Почему пайпы важны
Пайпы лежат в основе философии Unix. Они превращают набор разрозненных инструментов в живую экосистему, где команды сотрудничают, а не конкурируют.
Каждая команда делает одно маленькое дело — ищет, сортирует, считает, фильтрует. Но, соединяя их, вы получаете гибкие и мощные цепочки, способные решать реальные задачи без единого скрипта.
Можно сказать, пайпы — это «язык общения» между программами в Linux. С их помощью вы строите свои собственные инструменты из базовых деталей, как из строительных блоков.
Давайте попробуем составить команду, которая выведет только строки с предупреждениями (WARNING) из файла app.log, посчитает их количество и сохранит результат в файл warnings.txt:
Логические операторы
Мы научились соединять команды с помощью пайпов — так, чтобы результат одной становился входом для другой.
Но что, если нужно управлять порядком выполнения? Например, запустить следующую команду, только если предыдущая прошла успешно или, наоборот, только если она завершилась с ошибкой.
Для этого в Linux есть логические операторы:
&&, || и ; — простые, но мощные инструменты, которые превращают командную строку в мини-язык логики.
С помощью логических операторов можно описывать условные цепочки вида:
1grep "ERROR" app.log > errors.txt && echo "Ошибки найдены"
Здесь && позволяет вывести сообщения о найденных ошибках, только если их поиск и запись завершились успешно.
Мы помним, что каждая команда в Linux после выполнения отдаёт код возврата: 0 — успех, любое другое число — ошибка.
Именно по этому коду Bash решает, нужно ли выполнять следующую команду.
Пайпы передают данные, а логические операторы передают результат — успех или неудачу.
Давайте посмотрим, какие логические операторы нам доступны.
; — выполнить всё последовательно
Если команды разделены точкой с запятой (;), они выполняются последовательно, независимо от результата:
1mkdir reports; cd reports; touch summary.txt
Даже если каталог reports уже существует и команда mkdir выдаст ошибку — cd и touch всё равно выполнятся.
Это удобно, когда ошибки не критичны и нужно просто запустить несколько команд подряд.
&& — только если всё прошло успешно
Оператор && говорит: «Выполни следующую команду, только если предыдущая завершилась без ошибок».
1mkdir project && cd project
- Если каталог успешно создан — Bash перейдёт в него.
- Если
mkdirвернул ошибку —cdне запустится.
|| — только если была ошибка
Обратная логика: выполни следующую команду, только если предыдущая провалилась.
1cat report.txt || echo "Файл не найден"
- Если файл
report.txtсуществует —catпокажет его содержимое, аechoне выполнится. - Если же файла нет —
catвернёт ошибку, и Bash выведет сообщение «Файл не найден».
Комбинирование операторов
Мы можем объединить несколько операторов в одну цепочку команд:
1mkdir data && cd data || echo "Не удалось перейти в каталог"
Здесь:
mkdir data— создаёт каталог.&& cd data— выполнится, только если создание прошло успешно.|| echo ...— выполнится, если хоть что-то пошло не так.
В одну строку — полноценный скрипт с проверкой!
Теперь давайте разберём пример чуть сложнее.
Пример: автоматизируем работу с отчётами
Допустим, каждый день в каталоге ~/Reports формируются новые отчёты.
Нам нужно переносить их в другой каталог — ~/Projects/$(date +%Y-%m-%d) — для дальнейшей обработки, но только если каталог существует.
Если его нет — его нужно создать и повторить процесс.
Здесь date +%Y-%m-%d — команда, которая выводит текущую дату в формате ГГГГ-ММ-ДД. Например, 2025-12-03.
Шаг 1. Проверим, что каталог существует
Самый простой способ проверить, что каталог существует, — это попробовать перейти в него:
1alice@ubuntu:~$ cd ~/Projects/$(date +%Y-%m-%d)
2-bash: cd: /home/alice/Projects/2025-10-27: No such file or directory
Если мы проверим код возврата, мы увидим, что он отличается от 0:
1alice@ubuntu:~$ echo $?
21
Шаг 2. Создадим каталог
Мы хотим создать каталог, если его не существует. Какой именно оператор нам подойдёт?
&& позволяет выполнить следующую команду, если предыдущая завершилась успешно.
|| — если предыдущая завершилась с ошибкой.
Очевидно, что невозможность перейти через cd в несуществующий каталог не очень похоже на успех, поэтому давайте используем ||:
1alice@ubuntu:~$ cd ~/Projects/$(date +%Y-%m-%d) || mkdir -p ~/Projects/$(date +%Y-%m-%d)
2-bash: cd: /home/alice/Projects/2025-10-27: No such file or directory
Теперь Bash попробует перейти в каталог и, если каталог отсутствует, создаст его.
Обратите внимание: мы всё ещё получаем ошибку в выводе, хоть и создали каталог сразу после неё.
От этой ошибки можно избавиться, перенаправив вывод команды cd. Помните как?
Но, главное, мы можем убедиться, что каталог действительно создался:
1alice@ubuntu:~$ ls -d ~/Projects/$(date +%Y-%m-%d)
2/home/alice/Projects/2025-10-27
Шаг 3. Перенесём отчёты
Мы научились создавать каталог, если его не существует. А теперь добавим перенос отчётов:
1alice@ubuntu:~$ cd ~/Projects/$(date +%Y-%m-%d) || mkdir -p ~/Projects/$(date +%Y-%m-%d) && mv ~/Reports/* . ; echo "Готово!"
2-bash: cd: /home/alice/Projects/2025-10-27: No such file or directory
3Готово!
Для начала разберёмся с длинными командами. В терминале мы можем использовать обратный слеш \, для того чтобы перенести строку. Это помогает сделать длинную команду гораздо читабельнее.
Чтобы выполнить перенос на новую строку, нужно набрать \ и нажать Enter. Обратите внимание: приглашение командной строки сменится с $ на >:
1alice@ubuntu:~$ cd ~/Projects/$(date +%Y-%m-%d) || \<Enter>
2> mkdir -p ~/Projects/$(date +%Y-%m-%d) && \<Enter>
3> mv ~/Reports/* . ; echo "Готово!"
4-bash: cd: /home/alice/Projects/2025-10-27: No such file or directory
5Готово!
Вроде бы выглядит идеально, но если мы запустим эту команду, то увидим, что файлы перенеслись неправильно:
1alice@ubuntu:~$ ls -l ~/report*
2-rw-rw-r-- 1 alice alice 7 Oct 27 05:48 /home/alice/report1.pdf
3-rw-rw-r-- 1 alice alice 7 Oct 27 05:48 /home/alice/report2.pdf
4-rw-rw-r-- 1 alice alice 7 Oct 27 05:48 /home/alice/report3.pdf
Новый каталог действительно создался, а вот файлы переместились не туда, потому что команда cd не выполнилась и Bash продолжил работу в старом каталоге.
Посмотрим внимательнее на нашу команду:
1cd ~/Projects/$(date +%Y-%m-%d) || \
2mkdir -p ~/Projects/$(date +%Y-%m-%d) && \
3mv ~/Reports/* . ; echo "Готово!"
На первый взгляд, логично, но Bash читает её несколько иначе, чем мы.
В Bash операторы && и || имеют одинаковый приоритет и выполняются слева направо.
Точка с запятой (;) имеет меньший приоритет, то есть выполняется в самом конце.
Bash видит эту строку примерно так:
1((cd ... || mkdir ...) && mv ...) ; echo ...
То есть:
- Попробовать
cd. - Если не получилось — выполнить
mkdir. - Если
mkdirпрошёл успешно — сразу выполнитьmv. - Но при этом
cdвсё ещё не выполнился после создания папки!
В итоге mv запускается в старом каталоге и переносит отчёты прямо в /home/alice, а не в /home/alice/Projects/2025-10-27.
Шаг 4. Перенесём отчёты (ещё раз)
Самым простым решением будет добавить второй cd после создания каталога, и это на самом деле будет работать:
1cd ~/Projects/$(date +%Y-%m-%d) || \
2mkdir -p ~/Projects/$(date +%Y-%m-%d) && \
3cd ~/Projects/$(date +%Y-%m-%d) && \
4mv ~/Reports/* . ; \
5echo "Готово!"
Но если подумать, то первый cd нам не так уж и нужен, мы можем просто выполнить mkdir первым шагом:
1mkdir -p ~/Projects/$(date +%Y-%m-%d) && \
2cd ~/Projects/$(date +%Y-%m-%d) && \
3mv ~/Reports/* . ; \
4echo "Готово!"
Что происходит теперь:
- Если каталога не существует, то мы его создадим.
- Если каталог уже существует, это не проблема.
mkdirне будет создавать его ещё раз. - Далее мы перейдём в каталог и перенесём отчёты.
В принципе, на этом можно было бы остановиться: у нас есть рабочая цепочка команд, которая выполняет нашу задачу.
Но что, если нам нужно будет реализовать более сложный сценарий?
Пример: усложняем сценарий — перенос, архивирование и логирование
Мы хотим перенести отчёты в ~/Projects, но сохранить их в архив, а если что-то пойдёт не так — сохранить сообщения об ошибках.
Распишем план действий:
- Проверить, существует ли каталог
~/Projects/$(date +%Y-%m-%d). Если нет — создать. - Если каталог
~/Archiveсуществует, запаковать туда старые отчёты. - Если архивирование прошло успешно, перенести новые отчёты.
- В конце вывести сообщение об успехе, а если что-то пошло не так — записать ошибку в лог
~/Logs.
Теперь опишем логику:
1(
2 mkdir -p ~/Projects/$(date +%Y-%m-%d) &&
3 mkdir -p ~/Archive &&
4 cp ~/Reports/* ~/Archive/ 2>/dev/null &&
5 cd ~/Projects/$(date +%Y-%m-%d) &&
6 mv ~/Reports/*.pdf . &&
7 echo "Отчёты успешно перенесены и заархивированы"
8) || (
9 mkdir -p ~/Logs &&
10 echo "Что-то пошло не так: $(date)" >> ~/Logs/report-errors.log
11)
Мы уже научились писать команды в несколько строк, и здесь как раз приведён ещё один пример таких команд.
В терминале не обязательно писать всё в одну строку. Если команда длинная и содержит несколько шагов, Bash позволяет переносить её на новые строки, и это очень удобно для читаемости.
Когда вы вводите круглую скобку ( и нажимаете Enter, Bash понимает, что команда ещё не закончена. Приглашение командной строки сменится с $ на >. Чтобы завершить ввод команды, нужно вставить закрывающую скобку ):
1alice@ubuntu:~$ ( <Enter>
2> mkdir -p ~/Projects/$(date +%Y-%m-%d) <Enter>
3> cd ~/Projects/$(date +%Y-%m-%d) <Enter>
4> echo "Каталог готов!" <Enter>
5> ) <Enter>
6Каталог готов!
До тех пор, пока не появится закрывающая ), Bash будет считать, что ввод всё ещё продолжается. Поэтому каждая новая строка внутри блока воспринимается как часть одной команды, и \ для перехода на новую строку в этом случае не нужен.
Все строки между ( и ) выполняются как единый блок после закрывающей скобки. Такой способ помогает удобно оформлять сложные сценарии прямо в терминале.
Также обратите внимание на то, что мы не используем никакие операторы, а просто пишем следующую команду с новой строки.
Это равносильно использованию ;, и ту же самую команду мы можем описать так:
1(mkdir -p ~/Projects/$(date +%Y-%m-%d); cd ~/Projects/$(date +%Y-%m-%d); echo "Каталог готов!")
Но вернёмся к нашему примеру:
1(
2 mkdir -p ~/Projects/$(date +%Y-%m-%d) &&
3 mkdir -p ~/Archive &&
4 cp ~/Reports/* ~/Archive/ 2>/dev/null &&
5 cd ~/Projects/$(date +%Y-%m-%d) &&
6 mv ~/Reports/*.pdf . &&
7 echo "Отчёты успешно перенесены и заархивированы"
8) || (
9 mkdir -p ~/Logs &&
10 echo "Что-то пошло не так: $(date)" >> ~/Logs/report-errors.log
11)
Что здесь важно:
- Хоть это и выглядит как большой блок кода, это всё ещё цепочка команд, которую можно набрать и выполнить в терминале.
- Мы можем использовать круглые скобки, чтобы разделить две группы команд.
- Первая группа
(...)содержит весь основной сценарий — создание каталогов, перемещение и сообщение об успехе. - Вторая группа
(...)описывает альтернативный сценарий на случай, если первая группа вернёт ошибку.
Как это работает:
- Bash выполняет команды внутри первой группы слева направо. Если каждая из них успешна (код возврата
0), вторая группа после||не выполняется. - Если хоть одна команда внутри группы завершится с ошибкой (код, отличный от
0), первая группа возвращает ошибку и Bash выполняет вторую группу —echoв лог.
Если хотя бы один шаг в первой группе завершится неуспешно, Bash воспримет всю группу как неудачную и перейдёт к ветке справа от ||.
В результате, если наша цепочка команд выполнится успешно, мы увидим сообщение об успехе:
1Отчёты успешно перенесены и заархивированы
Но если что-то пойдёт не так, в файл ~/Logs/report-errors.log сохранится ошибка:
1alice@ubuntu:~/Logs$ cat report-errors.log
2Что-то пошло не так: Mon Oct 27 08:00:35 UTC 2025
Давайте попробуем составить команду, которая создаст каталог reports, если его нет, и выведет сообщение об успехе или, если создать не удалось, ошибку:
Введение в скрипты
К этому моменту вы уже умеете строить сложные цепочки команд, соединять их пайпами (|) и управлять порядком выполнения с помощью логических операторов (&&, ||, ;). По сути, это уже маленькие программы, только набранные прямо в терминале.
Например, вы могли написать такую строку:
1mkdir -p ~/Projects/$(date +%Y-%m-%d) && \
2cd ~/Projects/$(date +%Y-%m-%d) && \
3mv ~/Reports/* . && \
4echo "Готово!"
Эта команда выполняет несколько действий последовательно, проверяет ошибки и даже выводит сообщение об успехе — почти как скрипт.
Разница только в том, что вы набираете её вручную.
Когда стоит перейти к скриптам
Пока цепочка помещается в одну строку и её нужно выполнить один раз — можно обойтись пайпами и операторами.
Но если команда:
- содержит несколько строк и занимает пол-экрана,
- выполняется регулярно (каждый день, каждую неделю)
- или её нужно запустить на другом сервере —
значит, пора превращать её в bash-скрипт.
Скрипт — это просто текстовый файл, в котором собраны ваши команды в нужном порядке. Linux воспринимает его как обычную программу.
Например, наша длинная команда может превратиться в файл move_reports.sh:
1#!/bin/bash
2# Создаём каталог с сегодняшней датой
3mkdir -p ~/Projects/$(date +%Y-%m-%d)
4cd ~/Projects/$(date +%Y-%m-%d)
5
6# Переносим отчёты
7mv ~/Reports/* .
8
9# Печатаем сообщение об успехе
10echo "Отчёты успешно перенесены!"
Теперь, чтобы запустить всю последовательность, достаточно одной строки:
1bash move_reports.sh
Вся последовательность команд выполнится, как если бы мы набирали её вручную в терминале.
Как устроен скрипт
Первая строка в скрипте #!/bin/bash называется shebang (читается «шебанг»).
Она сообщает системе, какой интерпретатор нужно использовать для выполнения скрипта.
#! — это специальные символы, а путь после них (/bin/bash) указывает, какой именно интерпретатор должен запустить команды внутри файла.
|
Пример shebang |
Что делает |
|
|
Запускает скрипт через Bash |
|
|
Запускает через Python 3 |
|
|
Через стандартную оболочку sh |
|
|
Через Perl |
Если не указать shebang, Linux не будет знать, как выполнить файл напрямую, и придётся запускать его вручную через bash script.sh.
Строки, начинающиеся с #, — это комментарии.
Они не выполняются и нужны, чтобы пояснять другим людям, что делает тот или иной участок кода.
1# Создаём каталог, если его нет
2mkdir -p ~/Projects
3
4# Переходим в него
5cd ~/Projects
Комментарии — хорошая привычка. Они делают ваши скрипты понятными даже спустя месяцы.
Условные конструкции if
Условную конструкцию if удобно использовать тогда, когда логических операторов && и || становится слишком много и цепочка команд перестаёт быть читаемой.
Выглядит эта конструкция так:
1if [ условие ]; then
2 команда_1
3else
4 команда_2
5fi
if— проверяет условие в квадратных скобках[ ];then— указывает, какие команды выполнить, если условие истинно (успешный код —0);else— необязательная часть, выполняется при ложном условии (код ошибки);fi— закрывает блокif(это простоifнаоборот).
Давайте разберём следующую цепочку:
1grep "ERROR" app.log > /dev/null && echo "Ошибки найдены" || echo "Ошибок нет"
Мы можем представить её иначе, при помощи конструкции if:
1grep "ERROR" app.log > /dev/null
2if [ $? -eq 0 ]; then
3 echo "Ошибки найдены"
4else
5 echo "Ошибок нет"
6fi
grep "ERROR" app.log > /dev/null— ищет словоERRORв файле и ничего не выводит (всё отправляется в «чёрную дыру»/dev/null);$?— специальная переменная, которая хранит код возврата последней команды:0— команда прошла успешно (grepнашёл ошибки),1— ничего не найдено;
[ $? -eq 0 ]— проверяет, равно ли (-eq, или equal) значение$?нулю:- если да — выполняется
echo "Ошибки найдены", - иначе —
echo "Ошибок нет".
- если да — выполняется
Наша логика стала гораздо более читаемой, и нам будет проще добавлять новые условия в таком виде.
Как запустить скрипт
Скрипт можно запустить через команду bash, как мы уже увидели выше:
1alice@ubuntu:~$ bash move_reports.sh
В этом случае явно указывается интерпретатор, который должен выполнить файл. Такой способ работает даже без shebang в файле.
Но гораздо правильнее сделать скрипт исполняемым. По умолчанию любой созданный файл — обычный текстовый файл.
Linux не позволит запустить его как программу, пока вы явно не дадите ему право на исполнение.
Сделать это можно при помощи команды chmod +x file, таким образом мы добавляем к файлу специальный флаг (x, от англ. execute). Это позволяет оболочке понять, что этот файл можно запустить как программу:
1alice@ubuntu:~$ chmod +x move_reports.sh
И теперь его можно запустить напрямую:
1alice@ubuntu:~$ ./move_reports.sh
./ означает, что файл находится в текущем каталоге.
Если просто ввести move_reports.sh, Bash будет искать его в переменной $PATH (где лежат системные программы) и не найдёт, если вы не добавили туда свой текущий каталог.
В Linux любой файл запускается, только если его владелец явно дал разрешение на его выполнение. Подробнее о правах и разрешениях мы узнаем в главе 4.
Проверим, что всё работает:
1alice@ubuntu:~$ ./move_reports.sh
2Отчёты успешно перенесены!
Файл выполнился, все команды прошли последовательно, и при этом вы не набирали их вручную.
Почему это важно
Bash-скрипты — это следующий шаг после работы в терминале. Они позволяют:
- автоматизировать рутинные действия (резервное копирование, очистку логов, сборку проектов);
- запускать цепочки команд по расписанию (через
cron); - писать простые инструменты и мини-утилиты под свои нужды.
Внутри Bash-скрипта работают все знакомые вам инструменты:
- пайпы (
|) для передачи данных; - перенаправления (
>,>>,<) для ввода и вывода; - логические операторы (
;,&&,||) для условий; - а также переменные, циклы и аргументы команд.
Каждый скрипт — это просто набор знакомых вам команд, сохранённых в файле.
Так что освоение скриптов — не новый язык, а естественное продолжение всего, что вы уже умеете делать вручную.
Ключевые выводы по параграфу
- Пайп (
|) передаёт вывод одной команды (stdout) на вход другой (stdin), позволяя объединять простые утилиты в мощные цепочки без временных файлов. Несколько команд, соединённых пайпами, образуют конвейер (англ. pipeline). - Команда
xargsпревращает поток строк в аргументы для другой команды — это мост между командами, которые не умеют читатьstdin. - Принцип KISS (Keep It Simple, Stupid) — ключевая идея Unix: каждая программа должна делать одно дело, но хорошо. Соединяя такие программы пайпами, вы можете решать задачи любой сложности просто и красиво.
- Логические операторы управляют порядком выполнения команд:
;— выполнить все команды по порядку, независимо от ошибок;&&— выполнить следующую только при успехе предыдущей;||— выполнить следующую только при ошибке предыдущей.
- Круглые скобки
(...)позволяют группировать команды — Bash воспринимает их как единый блок. - Если цепочка команд становится длинной, повторяется или используется регулярно — пора превратить её в bash-скрипт: сохранить команды в файл и запускать одной строкой.
- В начале скрипта используется shebang (
#!/bin/bash), который указывает, каким интерпретатором выполнять команды. - Комментарии (
#) делают код понятным. Они игнорируются при выполнении, но помогают разобраться в логике. - Чтобы выполнить скрипт, можно:
- запустить его через
bash script.sh; - сделать исполняемым (
chmod +x script.sh) и запустить напрямую (./script.sh).
- запустить его через
Что дальше
Во второй главе вы прошли путь от самых первых команд в терминале до создания собственных цепочек и мини-скриптов.
Теперь вы уверенно ориентируетесь в командной строке Linux и понимаете, как взаимодействуют между собой команды, файлы и потоки данных.
Вы узнали:
- Как работают терминал и команды Linux: что такое командная оболочка, аргументы, опции и как получать помощь через
--helpиman. - Как ориентироваться в файловой системе: что такое абсолютные и относительные пути, специальные каталоги (
.,..,~) и как перемещаться между ними с помощьюcd,pwdиls. - Как создавать, копировать, перемещать и удалять файлы и каталоги с помощью
touch,mkdir,cp,mv,rmи шаблонов glob (*,?,[ ]). - Как читать и просматривать содержимое файлов через
cat,less,head,tailи как использоватьgrep,wc,diffдля анализа текста и поиска различий. - Как управлять потоками ввода-вывода: что такое
stdin,stdoutиstderr, как перенаправлять результаты и ошибки в файлы (>,>>,2>,2>&1,<) и как проверять результат выполнения команд с помощью кодов возврата. - Как соединять команды между собой с помощью пайпов (
|) и собирать из них конвейеры (grep | sort | uniq), а также управлять порядком их выполнения через логические операторы (;,&&,||). - Как создавать простейшие скрипты — сохранять команды в файл, использовать shebang (
#!/bin/bash), писать комментарии (#) и делать скрипт исполняемым (chmod +x script.sh).
И главное — вы познакомились с философией Unix:
Делай одно дело, но делай его хорошо. А всё остальное соединяй простыми и понятными средствами.
В следующей главе мы познакомимся с текстовыми редакторами в терминале: разберёмся, как редактировать файлы прямо из консоли с помощью nano и vim, как сохранять изменения и когда вообще стоит использовать такие редакторы.
А пока закрепите материал на практике:
- Отметьте, что урок прочитан, при помощи кнопки ниже.
- Пройдите мини-квиз, чтобы проверить, насколько хорошо вы разобрались с пайпами и логическими операторами.
- Перейдите к задачам этого параграфа и потренируйтесь.
- Перед этим — загляните в короткий гайд о том, как работает система проверки.
