Bash-скрипт — это когда вы однажды устали делать одно и то же вручную и решили что машина справится сама. Резервные копии, перезапуск сервисов, деплой, мониторинг, управление GPIO на Raspberry Pi — всё это автоматизируется парой десятков строк в текстовом файле. Звучит просто, но у начинающих всегда находится способ споткнуться: Permission denied, command not found, скрипт не запускается по крону хотя вручную работает отлично.
В этом руководстве разберём всё по порядку: как написать и запустить bash скрипт в Linux, как запускать скрипты через cron, как вызывать bash из Python, как работать с bash в Windows и как управлять GPIO на Raspberry Pi через shell-скрипты. Максимум практики, минимум воды.
Что такое bash скрипт и как он устроен
Bash скрипт — текстовый файл с последовательностью команд Linux/Unix, которые интерпретатор bash выполняет одну за другой. Никакой компиляции, никаких бинарников — просто файл с командами, которые вы и так могли бы набрать в терминале вручную.
Минимальный рабочий скрипт:
#!/bin/bash
echo "Hello Linux"
date
Первая строка — shebang (#!). Это не комментарий, это указание операционной системе каким интерпретатором выполнять файл. /bin/bash — путь к bash. Без shebang система не знает как запускать файл и либо откроет его в текстовом редакторе, либо выдаст ошибку.
Альтернативный вариант shebang, более переносимый между разными системами:
#!/usr/bin/env bash
Этот вариант находит bash через переменную PATH, что полезно когда bash может находиться в разных местах на разных системах (актуально для macOS, FreeBSD, некоторых Linux-дистрибутивов).
Структура типичного bash скрипта
#!/bin/bash
# Это комментарий - bash его игнорирует
# Переменные
NAME="Linux"
DATE=$(date +%Y-%m-%d)
# Вывод
echo "Привет, $NAME!"
echo "Сегодня: $DATE"
# Условие
if [ -f /etc/hosts ]; then
echo "Файл /etc/hosts существует"
else
echo "Файл не найден"
fi
# Цикл
for i in 1 2 3; do
echo "Итерация $i"
done
Сохраните это в файл script.sh — и переходим к запуску.
Запуск bash скрипта в Linux: три способа
Прежде чем запускать — скрипту нужно дать права на выполнение. Это делается один раз:
chmod +x script.sh
Команда chmod +x добавляет флаг исполняемости файлу. Без него Linux отказывается запускать файл как программу — даже если внутри правильный bash-код.
Способ 1: Прямой запуск
./script.sh
Точка и слеш перед именем файла означают «запустить файл из текущей директории». Без ./ bash будет искать script.sh в системных директориях из PATH и не найдёт его.
Способ 2: Через интерпретатор bash
bash script.sh
В этом случае права на выполнение не нужны — вы явно вызываете bash и передаёте ему файл как аргумент. Удобно для отладки и для запуска чужих скриптов без chmod.
Способ 3: Через полный путь
/home/user/scripts/script.sh
Нужен когда скрипт запускается не из его директории — например, из cron или systemd. Всегда используйте полные пути в автоматизации.
Проверка прав перед запуском
# Посмотреть права файла
ls -la script.sh
# -rwxr-xr-x 1 user user 45 Mar 12 10:00 script.sh
# Символ x (execute) должен присутствовать
# Дать права только владельцу
chmod u+x script.sh
# Дать права всем
chmod +x script.sh
# Или числовой вариант
chmod 755 script.sh
Bash скрипт для запуска программ и сервисов
Одно из самых популярных применений bash — запуск нескольких программ одной командой. Скрипт последовательно или параллельно запускает всё что нужно.
Запуск одного приложения
#!/bin/bash
# Открыть браузер
firefox https://google.com
# Запустить Python приложение
python3 /home/user/app/main.py
# Запустить Java приложение
java -jar /opt/myapp/app.jar
Запуск нескольких сервисов
#!/bin/bash
echo "=== Запуск сервисов ==="
echo "Время: $(date)"
# Запускаем nginx
systemctl start nginx
echo "nginx: запущен"
# Запускаем <a class="wpil_keyword_link" href="https://it-apteka.com/tag/docker/" target="_blank" rel="noopener" title="Docker" data-wpil-keyword-link="linked" data-wpil-monitor-id="786">Docker</a> контейнер
docker start my_container
echo "Docker контейнер: запущен"
# Запускаем Python воркер
cd /home/user/app
python3 worker.py &
echo "Python воркер: запущен (PID: $!)"
echo "=== Все сервисы запущены ==="
Символ & в конце команды запускает процесс в фоне. $! содержит PID последнего запущенного фонового процесса — полезно для логирования.
Скрипт с проверкой успешности запуска
#!/bin/bash
start_service() {
local service=$1
systemctl start $service
if systemctl is-active --quiet $service; then
echo "✓ $service запущен успешно"
else
echo "✗ Ошибка запуска $service"
exit 1
fi
}
start_service nginx
start_service postgresql
start_service redis
Запуск bash скрипта через cron
Cron — встроенный планировщик задач Linux. С его помощью скрипты запускаются автоматически по расписанию: каждую минуту, раз в час, раз в день, по будням в 3 ночи — как угодно.
Синтаксис cron
# Формат записи cron:
# минуты часы день_месяца месяц день_недели команда
# * * * * * /path/to/script.sh
# Примеры:
# 0 * * * * - каждый час (в 0 минут)
# */5 * * * * - каждые 5 минут
# 0 3 * * * - каждый день в 03:00
# 0 3 * * 1 - каждый понедельник в 03:00
# 0 3 1 * * - 1-го числа каждого месяца в 03:00
# 30 8 * * 1-5 - по будням в 8:30
Открыть редактор cron
crontab -e
Первый раз cron спросит какой редактор использовать — выбирайте nano если не знакомы с vim.
Практические примеры cron задач
# Бэкап базы данных каждую ночь в 3:00
0 3 * * * /home/user/scripts/backup_db.sh
# Очистка логов каждое воскресенье в 4:00
0 4 * * 0 /home/user/scripts/clean_logs.sh
# Мониторинг дискового пространства каждые 10 минут
*/10 * * * * /home/user/scripts/check_disk.sh
# Перезапуск сервиса каждый день в 6:00
0 6 * * * systemctl restart myapp
# Проверить список задач
crontab -l
# Удалить все задачи (осторожно!)
crontab -r
Почему скрипт не работает в cron хотя вручную работает
Классическая проблема: скрипт отлично запускается в терминале, но молчит в cron. Причина — у cron другое окружение: нет переменных PATH, HOME, USER которые есть в интерактивной сессии.
#!/bin/bash
# ВСЕГДА указывайте полные пути в cron-скриптах
# Не: python3 script.py
# А: /usr/bin/python3 /home/user/scripts/script.py
# Задайте PATH в начале скрипта
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Или добавьте PATH в начало crontab
# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# Перенаправляйте вывод в лог для отладки
0 3 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
Конструкция >> /var/log/backup.log 2>&1 записывает и обычный вывод, и ошибки в лог-файл. Незаменимо при отладке cron-задач.
Альтернатива cron: systemd timers
# Создать timer unit (более современный способ)
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/" target="_blank" rel="noopener" title="Резервное копирование" data-wpil-keyword-link="linked" data-wpil-monitor-id="790">Backup</a> Timer
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
# Включить и запустить
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
# Посмотреть все timers
systemctl list-timers
Запуск bash скрипта из Python
Python и bash отлично работают в паре. Python берёт на себя логику, обработку данных и API-взаимодействие, bash — системные операции, управление файлами и вызов утилит. Запустить bash из Python можно несколькими способами.
subprocess.run — рекомендуемый способ
import subprocess
# Простой запуск скрипта
result = subprocess.run(["bash", "script.sh"])
# Запуск с перехватом вывода
result = subprocess.run(
["bash", "script.sh"],
capture_output=True,
text=True
)
print("Вывод:", result.stdout)
print("Ошибки:", result.stderr)
print("Код возврата:", result.returncode)
# Запуск shell-команды строкой
result = subprocess.run(
"ls -la /home/user",
shell=True,
capture_output=True,
text=True
)
print(result.stdout)
subprocess.Popen — для долгих процессов
import subprocess
# Запуск в фоне с чтением вывода в реальном времени
process = subprocess.Popen(
["bash", "long_script.sh"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Читаем вывод по мере поступления
for line in process.stdout:
print(line, end="")
# Ждём завершения
process.wait()
print(f"Скрипт завершён с кодом: {process.returncode}")
os.system — простой но ограниченный способ
import os
# Простой запуск - возвращает только код возврата
exit_code = os.system("bash script.sh")
# Получить вывод через os.popen (устаревший способ)
output = os.popen("bash script.sh").read()
print(output)
os.system проще, но не даёт перехватить вывод напрямую. Для новых проектов используйте subprocess — он гибче и безопаснее.
Практический пример: Python запускает bash и обрабатывает результат
import subprocess
def run_script(script_path, *args):
"""Запустить bash скрипт и вернуть результат"""
cmd = ["bash", script_path] + list(args)
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30 # таймаут 30 секунд
)
if result.returncode != 0:
raise RuntimeError(f"Скрипт завершился с ошибкой:\n{result.stderr}")
return result.stdout.strip()
# Использование
try:
output = run_script("/home/user/scripts/backup.sh", "--database", "mydb")
print(f"Бэкап успешен: {output}")
except RuntimeError as e:
print(f"Ошибка: {e}")
except subprocess.TimeoutExpired:
print("Скрипт выполнялся слишком долго")
Запуск bash скриптов на Raspberry Pi
Raspberry Pi работает под управлением Linux (Raspberry Pi OS — это Debian), поэтому всё вышесказанное применимо напрямую. Но у RPi есть своя специфика — управление GPIO через bash-скрипты.
Управление GPIO через bash
#!/bin/bash
# Установка утилиты gpio (если не установлена)
# sudo apt install wiringpi
# Включить пин GPIO 17 как выход и установить HIGH
gpio -g mode 17 out
gpio -g write 17 1
echo "GPIO 17: включён"
sleep 2
# Установить LOW
gpio -g write 17 0
echo "GPIO 17: выключен"
Мигание светодиода — классический Hello World для GPIO
#!/bin/bash
PIN=17
# Настройка пина
gpio -g mode $PIN out
echo "Мигание светодиода на пине $PIN. Ctrl+C для остановки."
# Бесконечный цикл мигания
while true; do
gpio -g write $PIN 1
sleep 0.5
gpio -g write $PIN 0
sleep 0.5
done
Автозапуск скрипта на Raspberry Pi
Три основных способа автоматически запускать скрипт при загрузке RPi:
# Способ 1: cron с @reboot
crontab -e
# Добавить:
@reboot /home/pi/scripts/gpio_init.sh
# Способ 2: systemd service
# Создать файл /etc/systemd/system/gpio-init.service:
[Unit]
Description=GPIO Initialization
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/home/pi/scripts/gpio_init.sh
User=pi
[Install]
WantedBy=multi-user.target
# Включить:
sudo systemctl enable gpio-init.service
# Способ 3: /etc/rc.local (старый способ, всё ещё работает)
sudo nano /etc/rc.local
# Добавить перед строкой "exit 0":
/home/pi/scripts/gpio_init.sh &
Скрипт мониторинга температуры RPi
#!/bin/bash
# Получить температуру процессора
TEMP=$(vcgencmd measure_temp | grep -oP '\d+\.\d+')
echo "Температура CPU: ${TEMP}°C"
# Предупреждение при перегреве
THRESHOLD=70
if (( $(echo "$TEMP > $THRESHOLD" | bc -l) )); then
echo "ВНИМАНИЕ: Перегрев! $TEMP°C > $THRESHOLD°C"
# Отправить уведомление, включить вентилятор и т.д.
fi
Запуск bash скриптов в Windows
Windows — не Linux, но запускать bash-скрипты там вполне реально. Несколько способов на выбор в зависимости от задачи.
WSL (Windows Subsystem for Linux) — полноценный Linux в Windows
# Установка WSL (PowerShell от администратора)
wsl --install
# После перезагрузки - выбрать дистрибутив (по умолчанию Ubuntu)
# Запустить Linux окружение:
wsl
# Внутри WSL - обычный bash
bash script.sh
./script.sh
# <a href="https://it-apteka.com/powershell-skripty-v-windows-kak-sozdat-zapustit-i-avtomatizirovat-vypolnenie/" target="_blank" rel="noopener" data-wpil-monitor-id="799">Запустить bash скрипт из PowerShell</a> через WSL
wsl bash /home/user/script.sh
# Запустить скрипт Windows-пути через WSL
wsl bash "//c/Users/User/scripts/script.sh"
WSL — лучший способ если вам нужна полная совместимость с Linux-скриптами. Полноценное ядро Linux, apt, все системные утилиты.
Git Bash — простое решение для разработчиков
# Установить <a class="wpil_keyword_link" href="https://it-apteka.com/tag/git/" target="_blank" rel="noopener" title="Git" data-wpil-keyword-link="linked" data-wpil-monitor-id="788">Git</a> for Windows - в комплекте идёт Git Bash
# https://git-scm.com/download/win
# Запуск скрипта в Git Bash:
./script.sh
bash script.sh
# Запуск из командной строки Windows с Git Bash:
"C:\Program Files\Git\bin\bash.exe" script.sh
Git Bash — хороший выбор для разработчиков, которые уже используют Git. Не требует включения WSL, работает из коробки.
PowerShell + WSL
# Запустить bash скрипт из PowerShell
wsl bash script.sh
# Запустить с аргументами
wsl bash /path/to/script.sh arg1 arg2
# Получить вывод в переменную PowerShell
$output = wsl bash /path/to/script.sh
Write-Host $output
[/bash>
<h3>Cygwin - альтернатива WSL</h3>
<p>Cygwin предоставляет POSIX-совместимую среду для Windows. Старше WSL, но иногда полезен для совместимости. Установите с <a href="https://www.cygwin.com" target="_blank" rel="noopener">cygwin.com</a> и запускайте скрипты так же как в Linux.</p>
<h2>Типичные ошибки при запуске bash скриптов</h2>
<h3>Permission denied</h3>
# Ошибка:
bash: ./script.sh: Permission denied
# Решение: дать права на выполнение
chmod +x script.sh
# Или запустить явно через bash
bash script.sh
Command not found
# Ошибка:
./script.sh: line 5: python3: command not found
# Причина: команда не найдена в PATH (часто в cron)
# Решение: использовать полный путь
which python3 # узнать где находится python3
# /usr/bin/python3
# В скрипте писать полный путь:
/usr/bin/python3 app.py
# Или добавить PATH в начало скрипта:
PATH=/usr/local/bin:/usr/bin:/bin
Неправильный shebang или конец строки Windows
# Ошибка:
/bin/bash^M: bad interpreter
# Причина: файл создан в Windows (CRLF окончания строк)
# Решение: конвертировать в Unix формат (LF)
dos2unix script.sh
# Или через sed:
sed -i 's/\r//' script.sh
# Или через vim:
# :set ff=unix
# :wq
Скрипт не находит файлы с относительными путями
#!/bin/bash
# Проблема: скрипт ищет файлы относительно текущей директории,
# а не директории где лежит скрипт
# Решение: всегда переходить в директорию скрипта
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Теперь относительные пути работают корректно
cat config.txt
python3 helper.py
Скрипт не завершается при ошибке
#!/bin/bash
# По умолчанию bash продолжает выполнение даже при ошибках
# Добавьте в начало скрипта:
set -e # завершить при любой ошибке
set -u # ошибка при использовании неопределённой переменной
set -o pipefail # ошибка в pipeline = ошибка всей команды
# Или кратко:
set -euo pipefail
# Теперь если любая команда вернёт ненулевой код - скрипт остановится
Шпаргалка по запуску bash скриптов
# ===== СОЗДАНИЕ И ЗАПУСК =====
# Создать скрипт
nano script.sh
# Дать права
chmod +x script.sh
# Запустить
./script.sh
bash script.sh
/полный/путь/к/script.sh
# ===== CRON =====
# Редактировать расписание
crontab -e
# Посмотреть задачи
crontab -l
# Примеры расписания:
*/5 * * * * # каждые 5 минут
0 * * * * # каждый час
0 3 * * * # ежедневно в 3:00
0 3 * * 1 # каждый понедельник в 3:00
@reboot # при загрузке системы
# Запуск с логированием в cron:
0 3 * * * /home/user/script.sh >> /var/log/script.log 2>&1
# ===== PYTHON =====
# Запустить скрипт
import subprocess
subprocess.run(["bash", "script.sh"])
# С перехватом вывода
result = subprocess.run(["bash", "script.sh"], capture_output=True, text=True)
print(result.stdout)
# ===== WINDOWS =====
# WSL
wsl bash script.sh
# Git Bash
bash script.sh
# ===== RASPBERRY PI GPIO =====
gpio -g mode 17 out # пин как выход
gpio -g write 17 1 # установить HIGH
gpio -g write 17 0 # установить LOW
gpio -g read 17 # прочитать состояние
# ===== ОТЛАДКА =====
# Подробный вывод выполнения
bash -x script.sh
# Проверить синтаксис без выполнения
bash -n script.sh
# Безопасный режим в скрипте
set -euo pipefail
Заключение
Bash-скрипты - один из фундаментальных навыков системного администратора и DevOps-инженера. Они позволяют автоматизировать рутину, запускать сложные последовательности команд одним вызовом, планировать задачи через cron и интегрировать системные операции с Python-приложениями.
Главные правила которые сэкономят вам время:
- Всегда используйте полные пути в скриптах для cron и systemd - PATH там другой
- Перенаправляйте вывод в лог (
>> script.log 2>&1) при автоматическом запуске - иначе ошибки уходят в никуда
- Добавляйте
set -euo pipefail в начало скриптов - скрипт остановится при первой ошибке вместо того чтобы продолжать и ломать систему
- Тестируйте с
bash -x - режим трассировки покажет каждую выполняемую команду
Есть вопросы по конкретному сценарию автоматизации или столкнулись с непонятной ошибкой? Пишите в комментарии - разберём.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →




