Как стать автором
Поиск
Написать публикацию
Обновить
480.74
OTUS
Развиваем технологии, обучая их создателей

Гайд по timeout, watch и at: управление временем выполнения команд в Linux

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров3K

Как часто вы запускали какую‑нибудь штуку в терминале — и она зависала навсегда? Или наоборот: вам нужно было увидеть, как что‑то меняется каждую секунду, а вы упорно жали стрелку вверх и Enter? А может вы хотели запланировать задачу через 5 минут, но cron — это уже overkill?

Для всех этих сценариев в Linux есть три проверенных утилиты: timeout, watch, at. И, да, можно было бы обойтись скриптами и велосипедами, но... мы же не зря используем Unix‑подход, где всё уже давно придумано.

Сегодня мы рассмотрим: как управлять временем выполнения команд в Linux с помощью timeout, watch и at.

timeout

timeout — это часть пакета GNU coreutils. Его задача: запуск команды и слежка за временем. Если команда превышает лимит — она завершается по сигналу.

Сигнал по дефолту — SIGTERM, и это важно понимать: процессу дают шанс закончить работу корректно.

timeout 5s ./my_script.sh

Это значит: если my_script.sh не завершится через 5 секунд, он получит SIGTERM. Не завершится после — будет убит. Но только если вы явно попросите.

Как определить, почему команда завершилась?

Код возврата

Что означает

124

Команда убита по таймауту

125

Ошибка в timeout (например, нет команды)

126

Команда не исполняема

127

Команда не найдена

Остальное

Это код завершения самой команды

timeout 3s sleep 5
echo $?
# Выведет 124

Допустим, у вас не просто скрипт, а демон или сервер, и его нельзя гасить жёстко. Вы хотите сперва вежливо постучаться (SIGTERM), а если не открыл — вломиться (SIGKILL).

timeout --signal=SIGTERM --kill-after=2s 10s ./long_running_job

Через 10 секунд посылается SIGTERM. Если процесс не отреагировал — через 2 секунды ему прилетает SIGKILL.

Вот такая цепочка просто обязательна. Потому что просто убить процесс — полдела. Нужно дать ему шанс закрыть файлы, отписаться от сокетов и так далее.

Иногда вы хотите, чтобы timeout не вмешивался в exit‑код команды, если она завершилась сама. Тогда:

timeout --preserve-status 5s ./backup_script.sh

Без этого флага, даже если скрипт завершится с 0, timeout может вернуть 124 или 137 в зависимости от сигнала.

Безопасный curl с таймаутом:

if ! timeout 3s curl -s https://example.com > /dev/null; then
  echo "Request failed or timed out"
fi

Зачем делать так, если есть curl --max-time? Потому что timeout:

  • Универсален (не зависит от утилиты).

  • Может работать с любыми программами, а не только с сетевыми.

  • Позволяет детектировать не только долгую загрузку, но и зависание любого рода (например, если curl повис на DNS).

Дерево процессов

Если команда timeout запускает процесс, а тот — порождает других, то:

  • По дефолту timeout убьёт только первый процесс.

  • Все дочерние останутся жить, если они не завершатся сами.

Пример:

timeout 3s bash -c "sleep 5 & wait"

sleep останется жить даже после завершения bash, потому что он — отдельный процесс. Чтобы всё завершить как положено:

Вариант 1: setsid

timeout --foreground 3s setsid bash -c "sleep 5"=

setsid запускает процесс в новой session group. timeout может тогда убить всю группу сигналом.

Вариант 2: trap внутри скрипта

Если вы управляете кодом скрипта, можно повесить ловушку:

#!/bin/bash
trap "kill 0" EXIT
sleep 5 &
wait

И тогда timeout убьёт скрипт, а тот — всех своих детей.

Пример скрипта с логикой таймаута и логированием

#!/bin/bash

log_file="/var/log/mytask.log"
cmd="./long_task.sh"
timeout_sec=30

echo "[INFO] Running command with timeout ${timeout_sec}s" | tee -a "$log_file"

if timeout --kill-after=5s "$timeout_sec" "$cmd" >> "$log_file" 2>&1; then
  echo "[INFO] Command finished successfully" | tee -a "$log_file"
else
  status=$?
  if [ "$status" -eq 124 ]; then
    echo "[ERROR] Command timed out" | tee -a "$log_file"
  elif [ "$status" -eq 137 ]; then
    echo "[ERROR] Command killed forcefully" | tee -a "$log_file"
  else
    echo "[ERROR] Command failed with exit code $status" | tee -a "$log_file"
  fi
fi

Безопасно, читаемо, и можно засунуть хоть в Ansible, хоть в init‑скрипт.

watch

watch — это как cron. Он не ставит задачи в фон, он — показывает результат команды каждые N секунд.

watch -n 2 'команда'

-n 2 — интервал между запусками. 'команда' — в одиночных кавычках обязательно, если есть пайпы или сложность.

Пример:

watch -n 1 'ls -lh /var/log/myapp'

Полезные флаги

Минимум, который надо выучить и запомнить:

Флаг

Что делает

-n <сек>

Интервал между вызовами (по умолчанию 2 сек)

-d

Подсвечивает строки, которые изменились

-t

Убирает шапку (Every 2.0s: ... date/time)

-x

Не очищает экран между итерациями (--no-title)

-e

Завершить watch, если команда завершится с ошибкой

Разбор типовых сценариев

Мониторинг свободного места на диске:

watch -d -n 5 df -h /mnt/data

Идеально, если у вас летит выгрузка, миграция или ETL на пару гигабайт и вы не хотите остаться без диска.

Просмотр последних строк в логе:

watch -n 0.5 'tail -n 10 /var/log/syslog'

Полсекунды — нормальный интервал для чтения вживую. Если логи идут быстро, tail -F может быть даже полезнее, но watch проще в использовании.

Следим за systemd сервисом:

watch -n 1 'journalctl -u nginx.service -n 10 --no-pager'

--no-pager обязателен. Иначе journalctl залипнет, ожидая нажатия клавиш.

Приме

watch -n 1 'ps -eo pid,etime,%cpu,%mem,cmd --sort=-%cpu | head -10'

Это выводит топ-10 процессов по загрузке CPU.

Подсветка изменений: -d

Когда вы отслеживаете метрики (CPU, IO, tx/rx по сетевым интерфейсам), -d показывает только изменившиеся строки. Не нужно глазами искать, что именно поменялось.

Пример:

watch -d -n 1 'netstat -antp | grep ESTABLISHED'

Алиасы и привычки

В ~/.bash_aliases:

alias wtail="watch -n 1 'tail -n 20'"
alias wdf="watch -d -n 2 'df -h'"
alias wps="watch -n 1 'ps aux --sort=-%cpu | head -10'"

Быстрее вбить wps, чем каждый раз вспоминать ps aux и флаги сортировки.

Ограничения и особенности

По умолчанию watch пишет только stdout. Поэтому, если команда падает, вы можете не заметить. Чтобы всё увидеть:

watch -n 1 'команда 2>&1'

Если вы забыли кавычки, и в команде есть пайпы, watch может отработать странно или вообще не запуститься.

Плохо:

watch -n 1 tail -n 10 /var/log/syslog | grep error

Хорошо:

watch -n 1 'tail -n 10 /var/log/syslog | grep error'

Переходим к at.

at: запуск команды в будущем

Если cron — это про регулярность, то at — про раз и навсегда. Прямой, удобный, Unix‑way инструмент, если тебе нужно что‑то запустить через 5 минут и забыть. И не, не нужно писать костыльный sleep 300 && your_command &.

at не всегда стоит из коробки. Поэтому на Debian/Ubuntu:

sudo apt install at
sudo systemctl enable --now atd

Если не включить atd, то можно сколько угодно пихать задачи в очередь, но никто их исполнять не будет.

Синтаксис

Всё просто:

echo "команда" | at <время>

Или интерактивно:

at 10:30

И тебе открывается ввод команд (одна на строку), а завершение по Ctrl+D.

Примеры использования

Запись в файл через 2 минуты:

echo "echo 'Hello from the future' > ~/future.txt" | at now + 2 minutes

Перезапуск сервиса:

echo "sudo systemctl restart nginx" | at now + 10 minutes

Удаление временных файлов:

echo "rm -rf /tmp/my-temp" | at 03:00

Отправка уведомления себе:

echo "DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Сделай перерыв!'" | at now + 1 hour

notify-send требует правильных переменных окружения, особенно в десктоп‑средах. Сам at запускается как system user и без GUI‑окружения — нужно передать DISPLAY и DBUS_SESSION_BUS_ADDRESS вручную.

Как посмотреть очередь

atq

Пример вывода:

1   2025-07-28 12:30 a user
2   2025-07-28 14:00 a user

Где:

  • Первый столбец — ID задания

  • Время — когда оно будет выполнено

  • a — активное (ещё не исполнено)

Удалить задание

atrm <job_id>

Пример:

atrm 1

Или:

atq | grep rotate | awk '{print $1}' | xargs atrm

Удалит все задания, связанные с конкретным словом в команде (если ты хранишь логику именования).

Как это использовать

Плановый рестарт сервиса ночью:

echo "sudo systemctl restart myservice" | at 02:30

Бэкап текущей директории через 15 минут:

echo "tar czf ~/backup_$(date +\%Y\%m\%d).tar.gz $(pwd)" | at now + 15 minutes

Отправка сообщения себе в Telegram:

echo "curl -s -X POST https://api.telegram.org/bot$TOKEN/sendMessage -d chat_id=$CHAT -d text='Пора встать и пройтись'" | at now + 45 minutes

Альтернативные форматы времени

Вот что понимает at:

Формат

Пример

now + 1 hour

от текущего момента

midnight

сегодня в 00:00

teatime

сегодня в 16:00

tomorrow

завтра в 00:00

12:30

сегодня в 12:30

08/01/2025 13:00

точная дата и время

Если нужно быстро сделать задачу «отложить команду и забыть» используем at.


Итоги

timeout, watch и at — три скромные, но очень полезные утилиты, которые закрывают кучу сценариев: от убийства зависших процессов до live‑мониторинга и точечного запуска задач. Делитесь своими кейсами в комментариях.

Если вы работаете с Linux и стремитесь систематизировать знания — обратите внимание на курс Administrator Linux. На нем разбираем работу с процессами, планировщиками задач, службами и журналами — в том числе утилиты вроде timeout, watch и at.

Чтобы узнать больше о курсах по Linux и получить доступ к записям открытых уроков, переходите в телеграм‑бот.

Теги:
Хабы:
+33
Комментарии2

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS