es
Feedback
BashTex | Linux

BashTex | Linux

Ir al canal en Telegram

Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin

Mostrar más
2 529
Suscriptores
Sin datos24 horas
+367 días
+1930 días

Carga de datos en curso...

Canales Similares
Sin datos
¿Algún problema? Por favor, actualice la página o contacte a nuestro gerente de soporte.
Menciones Entrantes y Salientes
---
---
---
---
---
---
Atraer Suscriptores
agosto '26
agosto '26
+53
en 11 canales
julio '26
+16
en 1 canales
Get PRO
junio '26
+38
en 5 canales
Get PRO
mayo '26
+8
en 0 canales
Get PRO
abril '26
+14
en 0 canales
Get PRO
marzo '26
+22
en 0 canales
Get PRO
febrero '26
+18
en 0 canales
Get PRO
enero '26
+85
en 5 canales
Get PRO
diciembre '25
+122
en 19 canales
Get PRO
noviembre '25
+93
en 2 canales
Get PRO
octubre '25
+107
en 7 canales
Get PRO
septiembre '25
+197
en 20 canales
Get PRO
agosto '25
+16
en 1 canales
Get PRO
julio '25
+130
en 13 canales
Get PRO
junio '25
+57
en 4 canales
Get PRO
mayo '25
+70
en 1 canales
Get PRO
abril '25
+728
en 23 canales
Get PRO
marzo '25
+129
en 6 canales
Get PRO
febrero '25
+235
en 9 canales
Get PRO
enero '25
+497
en 19 canales
Get PRO
diciembre '24
+134
en 8 canales
Get PRO
noviembre '24
+819
en 17 canales
Fecha
Crecimiento de Suscriptores
Menciones
Canales
27 agosto0
26 agosto+2
25 agosto+1
24 agosto+1
23 agosto+4
22 agosto+9
21 agosto+26
20 agosto0
19 agosto0
18 agosto+1
17 agosto0
16 agosto+2
15 agosto0
14 agosto0
13 agosto0
12 agosto0
11 agosto+1
10 agosto0
09 agosto0
08 agosto0
07 agosto0
06 agosto0
05 agosto+4
04 agosto+2
03 agosto0
02 agosto0
01 agosto0
Publicaciones del Canal
/proc/PID/wchan: где именно завис процесс Если процесс показывает высокий D в ps, сам статус ещё не говорит, на чём именно он остановился. Для этого Linux предоставляет /proc/<PID>/wchan. ▪️Сначала найдём процесс
ps -eo pid,stat,wchan:30,cmd | grep '[D]'
Например:
2418 D    io_schedule     backup
Здесь wchan показывает kernel wait channel - функцию ядра, в которой процесс ожидает продолжения. ▪️Посмотреть напрямую
cat /proc/2418/wchan
Можно получить:
io_schedule
Или:
nfs_wait_bit_killable
Во втором случае уже появляется конкретная зацепка: процесс может ждать операции, связанной с NFS. ▪️Сравнить несколько процессов
for p in /proc/[0-9]*; do
    pid=${p##*/}
    state=$(awk '{print $3}' "$p/stat" 2>/dev/null)
    [ "$state" = D ] || continue

    printf '%-8s %s\n' \
        "$pid" \
        "$(cat "$p/wchan" 2>/dev/null)"
done
Получится примерно:
2418     io_schedule
2421     io_schedule
2517     nfs_wait_bit_killable
Если десятки процессов одновременно находятся в одном wchan, это уже может указывать на общий узкий участок. ▪️Посмотреть стек ядра Для более глубокого разбора:
cat /proc/2418/stack
Можно увидеть цепочку вызовов ядра, в которой находится поток. Например:
io_schedule
schedule
...
Доступ к /proc/PID/stack зависит от прав и настроек безопасности. ▪️Почему kill -9 иногда не помогает Процесс в состоянии D находится в uninterruptible sleep. Если он ждёт завершения определённой операции ядра, SIGKILL не может мгновенно вывести его из этого состояния. Поэтому вместо бесконечного:
kill -9 2418
полезнее сначала посмотреть:
ps -o pid,stat,wchan,cmd -p 2418
cat /proc/2418/wchan
cat /proc/2418/stack
Так можно понять, почему процесс не реагирует. ▪️Почему это важно ps показывает симптом - D. wchan помогает увидеть причину ожидания на уровне ядра. Для проблем с дисками, NFS, storage и зависшими I/O-операциями это один из самых быстрых способов перейти от «процесс завис» к пониманию, где именно он застрял. BashTex 📱 #bash #linux

2
BASH_SOURCE и FUNCNAME: как понять, откуда реально вызвали функцию В больших Bash-скриптах функция может вызываться из другой функции, которая пришла из отдельного файла. Обычный $0 в такой ситуации мало что объясняет. Для этого у Bash есть специальные массивы: BASH_SOURCE FUNCNAME ▪️Простой пример foo() { echo "file: ${BASH_SOURCE[0]}" echo "caller: ${BASH_SOURCE[1]}" echo "function: ${FUNCNAME[0]}" } foo BASH_SOURCE[0] показывает файл, в котором выполняется текущая функция, а BASH_SOURCE[1] - файл, откуда она была вызвана. ▪️Посмотреть весь стек вызовов foo() { printf '%s\n' "${FUNCNAME[@]}" } bar() { foo } bar Можно получить примерно: foo bar main То есть Bash хранит стек функций. ▪️Особенно полезно для логирования Например: log() { printf '[%s] %s: %s\n' \ "$(date '+%F %T')" \ "${FUNCNAME[1]}" \ "$*" } Теперь: deploy() { log "starting deployment" } deploy может вывести: [2026-08-24 12:30:01] deploy: starting deployment Лог сразу показывает, какая функция создала сообщение. ▪️А если скрипт состоит из нескольких файлов Допустим: main.sh lib.sh utils.sh и функции вызывают друг друга через source. Тогда: printf '%s\n' "${BASH_SOURCE[@]}" покажет цепочку файлов, через которые прошёл вызов. ▪️Чем это отличается от $0 echo "$0" обычно показывает имя запущенного скрипта или shell. А: echo "${BASH_SOURCE[0]}" показывает конкретный Bash-файл, где находится текущий код. Это особенно заметно при использовании: source lib.sh ▪️Почему это важно При отладке больших shell-проектов сообщение вроде: ERROR: failed почти бесполезно. А: ERROR: deploy() in lib/deploy.sh уже позволяет быстро найти источник проблемы. BASH_SOURCE и FUNCNAME превращают Bash-функции из непрозрачного набора вызовов в трассируемый стек. BashTex 📱 #bash #linux
165
3
mapfile: как загружать вывод команды в массив без while read Если результат команды нужно сохранить построчно, часто используют: items=() while IFS= read -r line; do items+=("$line") done < <(some_command) В Bash для этого есть встроенный mapfile. ▪️Простой вариант mapfile -t files < <(find /tmp -type f) Теперь каждая строка - отдельный элемент массива: printf '%s\n' "${files[@]}" -t убирает символ переноса строки из каждого элемента. ▪️Зачем это лучше Нет отдельного while, нет проблемы с subshell и не нужно вручную добавлять элементы: mapfile -t users < <(cut -d: -f1 /etc/passwd) echo "Users: ${#users[@]}" Можно обращаться к конкретному элементу: echo "${users[0]}" ▪️Можно читать файл напрямую mapfile -t lines < config.txt А затем: for line in "${lines[@]}"; do printf 'CONFIG: %s\n' "$line" done ▪️Читать только часть вывода Например: mapfile -t -n 10 lines < access.log В массив попадут только первые 10 строк. ▪️Есть важный нюанс mapfile ориентирован именно на разделённые строки данные. Если содержимое может содержать \n внутри логического элемента, обычный построчный массив уже не подходит. Для других разделителей можно использовать -d: mapfile -d ',' -t items < data.txt Теперь разделителем считается запятая. ▪️Где это удобно Например, получить список systemd-сервисов: mapfile -t services < <( systemctl list-units --type=service --state=running --no-legend | awk '{print $1}' ) После этого массив можно безопасно передавать дальше: for service in "${services[@]}"; do systemctl is-active "$service" done ▪️Почему это важно mapfile - встроенный механизм Bash для преобразования потоковых данных в массив. Он делает код короче и избавляет от целого класса проблем, связанных с while read, пайпами и сохранением переменных. BashTex 📱 #bash #linux
226
4
noclobber: как запретить Bash случайно перезаписать файл Обычный оператор: echo "new config" > config.txt без предупреждения удалит старое содержимое config.txt. Для скриптов, где случайная перезапись критичного файла недопустима, в Bash есть режим noclobber. ▪️Включаем его set -o noclobber Теперь: echo "new config" > config.txt если файл уже существует, завершится ошибкой: bash: config.txt: cannot overwrite existing file ▪️Проверить состояние set -o | grep noclobber Или короткая форма: set -C Выключить обратно: set +C ▪️Но >> продолжает работать noclobber защищает именно от перезаписи через >: echo "new line" >> config.txt добавит данные в конец файла. ▪️А как принудительно перезаписать? Есть специальная конструкция: echo "new config" >| config.txt Оператор >| игнорирует noclobber и разрешает перезапись. Это удобно, когда защита включена глобально, но конкретный участок скрипта действительно должен заменить файл. ▪️Где это полезно Например, скрипт генерирует конфигурацию: set -C generate_config > /etc/myapp/config.conf Если файл уже существует, операция остановится вместо того, чтобы молча уничтожить текущую конфигурацию. ▪️Важный нюанс noclobber не является полноценной защитой от всех способов изменения файла. Это именно настройка shell для оператора >. Её задача проще: превратить потенциально опасную случайную перезапись в явную ошибку. ▪️Почему это важно В Bash > выглядит безобидно, но фактически означает «открыть файл на запись и обнулить его». noclobber позволяет добавить дополнительный барьер там, где потеря существующего содержимого недопустима. BashTex 📱 #bash #linux
249
5
nullglob: когда *.log внезапно превращается в строку Bash умеет раскрывать glob-шаблоны: *.log Но если файлов с таким расширением нет, по умолчанию шаблон может остаться обычной строкой. Например: for file in /var/log/*.log; do echo "$file" done Если .log файлов нет, цикл может получить буквально: /var/log/*.log Это легко превращается в ошибку в автоматизации. ▪️Включаем nullglob shopt -s nullglob Теперь шаблон без совпадений исчезает: files=(/var/log/*.log) echo "${#files[@]}" Если файлов нет: 0 А не 1 с буквальным *.log. ▪️Почему это важно для массивов Без nullglob: files=(/tmp/*.tmp) может создать массив из одного элемента: /tmp/*.tmp После: shopt -s nullglob получаем действительно пустой массив. ▪️nullglob влияет только на текущий shell Его можно включить локально внутри функции: check_logs() { local old_nullglob shopt -q nullglob old_nullglob=$? shopt -s nullglob files=(/var/log/*.log) ((${#files[@]})) && printf '%s\n' "${files[@]}" ((old_nullglob == 0)) || shopt -u nullglob } Но для простых скриптов обычно достаточно включить настройку в начале. ▪️А если отсутствие файлов должно быть ошибкой? Тогда существует противоположная настройка: shopt -s failglob При отсутствии совпадений Bash выдаст ошибку вместо передачи буквального шаблона дальше. ▪️Почему это важно Glob кажется простой частью Bash, пока скрипт не запускается на сервере, где ожидаемых файлов просто нет. nullglob позволяет отличить настоящий список файлов от строки с нераскрытым шаблоном - особенно полезно при работе с массивами, логами и автоматической обработкой каталогов. BashTex 📱 #bash #linux
414
6
lastpipe: как Bash меняет поведение последней команды в pipeline Одна из особенностей Bash - команды в pipeline обычно выполняются в отдельных subshell. Из-за этого переменные, изменённые внутри while, после цикла могут не сохраниться. Например: count=0 printf '%s\n' a b c | while read -r line; do ((count++)) done echo "$count" В некоторых случаях результатом будет 0. Но у Bash есть настройка lastpipe, которая меняет это поведение. ▪️Включаем lastpipe shopt -s lastpipe Теперь последняя команда pipeline может выполняться в текущем shell. count=0 printf '%s\n' a b c | while read -r line; do ((count++)) done echo "$count" При подходящих условиях получим: 3 ▪️Есть важное условие lastpipe работает только когда job control отключён: set +m shopt -s lastpipe Именно поэтому поведение может отличаться между интерактивным Bash и обычным скриптом. ▪️Проверить настройку shopt lastpipe Получим: lastpipe off или: lastpipe on ▪️Почему это не универсальная замена Вместо изменения поведения shell часто проще использовать редирект: while read -r line; do ((count++)) done < <(printf '%s\n' a b c) Так код не зависит от настройки lastpipe. ▪️Где lastpipe действительно интересен Это полезно знать при разборе чужих Bash-скриптов: одна и та же конструкция с pipeline может вести себя по-разному в зависимости от настроек shell и режима job control. lastpipe - хороший пример того, как небольшая опция Bash меняет фундаментальное поведение pipeline и область видимости изменений. BashTex 📱 #bash #linux
396
7
АЙТИШНИКИ БЕСПЛАТНОЕ ОБУЧЕНИЕ Проект «Terminal» стал крупнейшей библиотекой бесплатного образования. В одном канале собраны к
АЙТИШНИКИ БЕСПЛАТНОЕ ОБУЧЕНИЕ Проект «Terminal» стал крупнейшей библиотекой бесплатного образования. В одном канале собраны курсы, книги, полезные инструменты и практические тренажёры для всех разработчиков: • Практические курсы и задания • Книги и статьи известных авторов • Полезные инструменты и ресурсы • IT-новости и инсайды Обучение по всем направлениям: SQL, Python, ML, Frontend, PHP, C++, Go, Git, Linux, QA, Java, Vibe-coding, InfoSec и др. ⌨️ подписаться
281
8
$? после &&, || и if: почему код возврата Bash бывает неочевидным В Bash $? содержит код завершения последней выполненной команды. Но конструкции &&, || и if меняют то, какие команды вообще выполняются. ▪️Простая цепочка false && echo "OK" echo $? echo после && не запускается, потому что false вернула 1. Итоговый $? - 1. А здесь: true && echo "OK" echo $? последней командой становится echo "OK", поэтому $? уже равен 0. ▪️С || ситуация аналогичная false || echo "fallback" echo $? false запускается, затем выполняется echo, потому что первая команда завершилась ошибкой. В итоге $? будет 0 - код echo, а не false. Если нужен исходный результат: false status=$? if [ "$status" -ne 0 ]; then echo "Command failed: $status" fi ▪️Почему if не создаёт обычный 1 if false; then echo "success" fi echo $? Здесь if возвращает статус последней выполненной команды в выбранной ветке. Если ни одна ветка не выполнялась, результатом становится код проверки условия. ▪️Частая ошибка command || echo "failed" status=$? status здесь содержит результат echo, а не command. Если важно сохранить код ошибки: if ! command; then status=$? fi Но здесь есть ещё один нюанс: ! инвертирует статус, поэтому для получения исходного кода лучше сохранять его непосредственно после команды. Например: command status=$? if [ "$status" -ne 0 ]; then echo "failed: $status" fi ▪️Почему это важно В сложных Bash-скриптах $? легко прочитать слишком поздно. Любая следующая команда - даже echo, printf или test - может заменить предыдущий код возврата. Поэтому если результат команды важен, сохраняйте его сразу. BashTex 📱 #bash #linux
388
9
return против exit: как не завершить весь скрипт внутри функции В Bash функция может вернуть управление вызывающему коду или полностью завершить текущий shell. Эти два случая часто путают. ▪️exit завершает весь скрипт check_config() { if [ ! -f config.conf ]; then echo "Config not found" exit 1 fi } check_config echo "Продолжаем работу" Если config.conf нет, выполнение закончится прямо внутри функции. Строка Продолжаем работу никогда не выполнится. ▪️return завершает только функцию check_config() { if [ ! -f config.conf ]; then echo "Config not found" return 1 fi } check_config echo "Продолжаем работу" Теперь функция завершилась с кодом 1, но сам скрипт продолжил выполнение. Именно поэтому return обычно используют внутри функций, а exit - на уровне самого скрипта. ▪️Код возврата можно обработать if check_config; then echo "Config OK" else echo "Config error" fi Или сохранить результат: check_config status=$? if [ "$status" -ne 0 ]; then echo "Ошибка: $status" fi ▪️Важный нюанс return можно использовать только внутри функции или в контексте, где Bash допускает возврат из текущего sourced-файла. Например: return 1 в обычном запускаемом скрипте верхнего уровня приведёт к ошибке: return: can only `return' from a function ▪️Типичная ошибка deploy() { check_config || exit 1 start_service } Если deploy вызывается из другого скрипта как функция, exit завершит уже весь shell. Чаще правильнее: deploy() { check_config || return 1 start_service } А решение о завершении оставить вызывающему коду: deploy || exit 1 Так функция сообщает об ошибке, а не решает за весь скрипт, что делать дальше. BashTex 📱 #bash #linux
322
10
/proc/PID/cwd: когда процесс работает не из того каталога У каждого Linux-процесса есть текущий рабочий каталог. Обычно мы видим его через shell, но для уже запущенного процесса можно посмотреть напрямую через /proc. readlink /proc/1234/cwd Например: /var/lib/app Это каталог, относительно которого процесс разрешает относительные пути. ▪️Найти процессы с неожиданным cwd for p in /proc/[0-9]*; do pid=${p##*/} cwd=$(readlink "$p/cwd" 2>/dev/null) || continue printf '%-8s %s\n' "$pid" "$cwd" done Теперь можно быстро увидеть, какие процессы находятся в /tmp, /dev/shm, домашних каталогах или других необычных местах. ▪️Почему это может быть важно Приложение может запускаться из: /opt/myapp но после работы оказаться в другом каталоге из-за chdir(). Особенно интересны процессы с: /tmp /dev/shm /home/user если для конкретного сервиса такое поведение не ожидается. ▪️Связь с systemd Для unit можно задать: [Service] WorkingDirectory=/var/lib/myapp А фактическое значение уже запущенного процесса проверить через /proc. Это полезнее, чем тупо смотреть конфигурацию: /proc/PID/cwd показывает состояние конкретного процесса прямо сейчас. BashTex 📱 #bash #SSH
398
11
📘 DevOps-инженер: от основ до продакшена — новый курс на Mentorix Пишете код, но деплой и прод — чёрный ящик? Курс про инжен
📘 DevOps-инженер: от основ до продакшена — новый курс на Mentorix Пишете код, но деплой и прод — чёрный ящик? Курс про инженерию эксплуатации: от первого сервера до автоматизированной CI/CD-инфраструктуры, которая держит нагрузку и не падает. ⚙️ Стек: Docker, Kubernetes, Ansible, Terraform, CI/CD, Prometheus + Grafana, логи ELK 🧩 Задания с автопроверкой + лабы в настоящем терминале 💻 Финальный проект в портфолио 🎓 Сертификат · доступ навсегда 🏷 −35% по промокоду DEVOPS35 — только 48 часов 13 990 ₽ → 9 094 ₽ ➜ Забрать курс со скидкой ━━━━━━━━━━━━━━ 🎁 А ещё на платформе — бесплатные курсы: Языки программирования ⚡ Golang — основы языка 🦀 Rust — основы языка 🐍 Основы Python Инфраструктура и DevOps 🖥 Основы DevOps 🐳 Docker: первые шаги 🔧 Git для начинающих Базы данных • SQL с нуля • MongoDB с нуля 📚 Все бесплатные курсы Mentorix
290
12
setsid: как действительно отделить процесс от SSH-сессии nohup часто используют, чтобы процесс пережил закрытие терминала: nohup ./backup.sh & Но nohup в основном защищает от SIGHUP. Сам процесс всё ещё может оставаться связанным с текущей session. Для полного создания новой сессии существует setsid. ▪️Что происходит обычно После подключения по SSH можно посмотреть: ps -o pid,ppid,sid,tty,cmd У процессов будут общие SID и управляющий терминал. При закрытии SSH это может иметь значение для поведения процессов. ▪️Запуск через setsid setsid ./backup.sh Процесс становится лидером новой session и получает новый SID. Проверить: ps -o pid,ppid,sid,tty,cmd -C backup.sh Можно увидеть, что SID процесса больше не связан с вашей SSH-сессией. ▪️Почему одного & недостаточно ./backup.sh & Фоновый процесс всё равно является частью текущей shell-сессии. & лишь не блокирует терминал ожиданием процесса. ▪️А nohup? nohup ./backup.sh & Это уже лучше для сценария с закрытием терминала, но nohup и setsid решают разные задачи. nohup изменяет обработку SIGHUP и обычно перенаправляет стандартный вывод. setsid создаёт новую session и отделяет процесс от текущей session. ▪️Комбинация Для простого запуска полностью отдельно: setsid ./backup.sh >/tmp/backup.log 2>&1 < /dev/null & Теперь процесс не зависит от стандартного ввода текущего терминала и находится в отдельной session. ▪️Где это полезно • запуск долгих задач из SSH; • анализ поведения daemon-like процессов; • shell-обвязки для сервисов; • понимание PID, PPID, SID и TTY. ▪️Почему это важно &, nohup и setsid часто воспринимают как взаимозаменяемые способы “запустить в фоне”. На самом деле они меняют разные свойства процесса. Если нужно понять, почему программа продолжает зависеть от SSH или терминала, смотреть стоит не только на PID, но и на SID, PPID и управляющий TTY. BashTex 📱 #bash #SSH
486
13
/proc/PID/environ: какие переменные реально получил процесс env показывает окружение текущего shell. Но для диагностики сервиса этого недостаточно: после запуска процесс мог получить совсем другой набор переменных. Linux хранит окружение каждого процесса здесь: cat /proc/1234/environ Значения разделены не переносами строк, а нулевыми байтами, поэтому удобнее: tr '\0' '\n' < /proc/1234/environ ▪️Найти конкретную переменную tr '\0' '\n' < /proc/1234/environ | grep '^PATH=' Или проверить настройки прокси: tr '\0' '\n' < /proc/1234/environ | grep -Ei 'proxy|http_proxy|https_proxy' ▪️Сравнить окружение двух процессов Например, приложение запущено вручную и через systemd: tr '\0' '\n' < /proc/1234/environ | sort > /tmp/app.env tr '\0' '\n' < /proc/5678/environ | sort > /tmp/service.env diff -u /tmp/app.env /tmp/service.env Так можно быстро найти переменную, из-за которой одна версия работает, а другая нет. ▪️Почему env может вводить в заблуждение Вы выполняете: echo "$PATH" и видите одно значение. Но процесс, запущенный несколько часов назад, мог получить старый PATH. Изменение конфигурации shell не меняет окружение уже работающего процесса. То же касается: HTTP_PROXY LD_LIBRARY_PATH LANG HOME JAVA_HOME ▪️Важный момент Чтение /proc/PID/environ требует соответствующих прав доступа. Для чужих процессов доступ может быть ограничен настройками безопасности системы. И главное: вывод нельзя бездумно отправлять в логи. В окружении могут находиться токены, пароли и другие секреты. ▪️Почему это важно Когда сервис работает “вручную”, но ломается под systemd, cron или другим менеджером процессов, часто проверяют конфигурационные файлы, но забывают про окружение. /proc/PID/environ показывает именно то, что получил уже запущенный процесс - а не то, что, как кажется, должно было быть передано ему. BashTex 📱 #bash #linux
466
14
/proc/PID/fd: как узнать, что на самом деле держит процесс ps показывает процесс, lsof умеет искать открытые файлы. Но иногда быстрее посмотреть непосредственно в /proc. У каждого процесса есть каталог: ls -l /proc/1234/fd Внутри находятся символические ссылки на все открытые файловые дескрипторы процесса. ▪️Что можно увидеть Например: 0 -> /dev/null 1 -> /var/log/app.log 2 -> /var/log/app.err 5 -> /tmp/data.db 0, 1 и 2 — стандартные stdin, stdout и stderr. Остальные дескрипторы приложение открыло самостоятельно. ▪️Найти удалённые файлы Особенно полезен такой поиск: ls -l /proc/1234/fd | grep deleted Можно обнаружить: 7 -> /var/log/app.log (deleted) Файл уже отсутствует в каталоге, но процесс всё ещё держит его открытым. ▪️Посмотреть тип дескриптора readlink /proc/1234/fd/7 Результат может быть: socket:[48192] или: pipe:[72831] или: /run/app.sock То есть через один каталог можно увидеть не только обычные файлы, но и сокеты, pipes и другие объекты ядра. ▪️Найти все открытые дескрипторы процесса for fd in /proc/1234/fd/*; do printf '%s -> %s\n' "${fd##*/}" "$(readlink "$fd")" done Получается компактная карта того, с какими объектами взаимодействует процесс. ▪️Почему это важно Если сервис ведёт себя странно, полезно посмотреть не только его аргументы и память, но и то, что он реально держит открытым. /proc/PID/fd помогает быстро найти удалённые логи, неожиданные файлы, UNIX-сокеты и каналы IPC - причём без отдельного инструмента анализа. BashTex 📱 #bash #utils
399
15
PIPESTATUS: как узнать, какая команда в пайпе реально упала В Bash есть неприятная особенность: после пайпа $? показывает статус только последней команды. Например: false | grep test echo $? Здесь grep успешно отработал и вернул 0, поэтому ошибка false потерялась. ▪️На помощь приходит PIPESTATUS Сразу после выполнения пайпа: false | grep test echo "${PIPESTATUS[@]}" Получим: 1 1 Каждое значение соответствует своей команде. false → 1 grep test → 1 ▪️Более наглядный пример cat /missing/file | grep ERROR | sort echo "${PIPESTATUS[@]}" Можно получить: 1 1 0 То есть cat завершился с ошибкой, grep ничего не получил, а sort технически выполнился успешно. ▪️Важный нюанс PIPESTATUS очень легко потерять: false | true echo "${PIPESTATUS[@]}" работает. Но если между пайпом и проверкой выполнить другую команду: false | true echo "check" echo "${PIPESTATUS[@]}" теперь PIPESTATUS относится уже к echo "check". Поэтому сохраняйте его сразу: false | true status=("${PIPESTATUS[@]}") echo "${status[@]}" ▪️А если нужен общий статус пайпа? Есть: set -o pipefail Теперь: false | true echo $? вернёт ненулевой код. Но pipefail отвечает только на вопрос «пайп завершился успешно?», а PIPESTATUS позволяет понять «какая именно команда вернула ошибку?». ▪️Почему это важно В сложных Bash-конвейерах последняя команда может успешно завершиться даже тогда, когда предыдущая уже упала. PIPESTATUS позволяет не терять эти ошибки и точно определить проблемный этап. BashTex 📱 #bash #utils
394
16
Проверка времени отклика сервисов Когда сервис работает, но пользователи жалуются на медлительность, нужно мерить не аптайм, а отклик. Это легко сделать обычным curl. ▪️ Самый простой замер time curl -s https://bashtex.com > /dev/null Показывает общее время выполнения запроса и быстро понять, тормозит или нет. ▪️ Точнее: только сетевое время curl -s -o /dev/null -w "time_total: %{time_total}\n" https://bashtex.com Полезные метрики: time_namelookup time_connect time_starttransfer time_total Пример: curl -w "DNS:%{time_namelookup} CONNECT:%{time_connect} TTFB:%{time_starttransfer} TOTAL:%{time_total}\n" \ -o /dev/null -s https://bashtex.com ▪️ Проверка нескольких сервисов for url in https://a.ru https://b.ru; do curl -o /dev/null -s -w "$url %{time_total}\n" "$url" done ▪️ Таймаут обязателен curl --connect-timeout 3 --max-time 5 https://bashtex.com Без таймаута любой мониторинг бесполезен. BashTex 📱 #bash #utils
483
17
eval: почему опасен и когда реально нужен В Bash почти любую строку можно превратить в команду через eval. Он берёт переданный текст и запускает его как новый Bash-код. Именно поэтому это один из самых спорных инструментов в shell-скриптах. ▪️Как работает eval Простой пример: cmd="ls -la" eval "$cmd" Bash сначала получает строку: ls -la а затем выполняет её как обычную команду. ▪️Где возникает проблема Если данные приходят от пользователя: read input eval "$input" Теперь введённый текст становится кодом. Например, вместо ожидаемого аргумента можно передать дополнительные команды, которые Bash выполнит при обработке строки. Главная проблема eval - повторный разбор данных как команд. ▪️Почему кавычки не всегда спасают Например: file="test.txt" eval "cat $file" Пока значение простое - всё работает. Но если внутри появятся специальные символы: file="file name.txt" или другие управляющие конструкции, поведение может отличаться от ожидаемого. ▪️Частая замена eval Вместо хранения команды строкой: cmd="ls -la /tmp" eval "$cmd" лучше использовать массив: cmd=(ls -la /tmp) "${cmd[@]}" Теперь Bash хранит команду и аргументы отдельно. ▪️Когда eval действительно нужен Иногда без него сложно обойтись. Например, при работе с динамическими именами переменных: name="USER" eval "echo \$$name" Но даже здесь часто есть более безопасные варианты: declare -n ref="$name" echo "$ref" ▪️Проверка перед использованием Если всё же нужен eval, данные должны быть полностью контролируемыми: case "$action" in start|stop|restart) eval "service_$action" ;; esac Здесь значение ограничено заранее известным списком. ▪️Почему это важно eval не является “плохой” командой сам по себе. Проблема появляется, когда данные превращаются в код. В большинстве случаев массивы, case, функции или declare решают задачу безопаснее. eval стоит оставлять только для ситуаций, где действительно нужен второй проход парсинга Bash. BashTex 📱 #eval
540
18
Проверка доступности локальных Unix-сервисов: nc -U и curl --unix-socket Не все сервисы в Linux слушают TCP-порты. Многие приложения работают только через UNIX-сокеты - локальные каналы связи между процессами. Например: /run/docker.sock /run/php/php-fpm.sock /run/containerd/containerd.sock Снаружи такие сервисы не видны через обычный nmap, но внутри системы они могут иметь важное значение. ▪️Найти активные UNIX-сокеты ss -xl Пример: u_str LISTEN 0 128 /run/docker.sock Теперь можно проверить, отвечает ли сервис. ▪️Проверка через nc Netcat умеет подключаться к UNIX-сокетам: nc -U /run/docker.sock Если соединение установлено - сокет доступен. Для HTTP-сервисов можно отправить запрос вручную: printf "GET / HTTP/1.0\r\n\r\n" | nc -U /run/service.sock ▪️Проверка HTTP API через curl Многие локальные API работают через UNIX-сокеты: curl --unix-socket /run/docker.sock http://localhost/version Здесь localhost не используется как сетевой адрес - он нужен только для формирования HTTP-запроса. ▪️Пример с Docker Вместо: docker version можно напрямую обратиться к API: curl --unix-socket /var/run/docker.sock \ http://localhost/_ping Ответ: OK означает, что Docker daemon доступен. ▪️Проверка прав доступа Даже если сервис работает, доступ может быть ограничен: ls -l /run/docker.sock Например: srw-rw---- root docker docker.sock Только пользователи группы docker смогут подключиться. ▪️Почему это важно UNIX-сокеты часто остаются вне стандартного мониторинга. Администратор проверяет открытые порты, но забывает про локальные API и IPC-каналы. Проверка через ss, nc -U и curl --unix-socket помогает быстро понять: жив ли сервис; кто имеет к нему доступ; отвечает ли локальное API; нет ли неожиданного открытого канала между процессами. BashTex 📱 #sort
542
19
Почему sort | uniq не всегда работает так, как ожидается Когда нужно убрать дубликаты, многие сразу пишут: sort file.txt | uniq Работает. Но только если понимать, что именно делает каждая команда. ▪️Как работает uniq Распространённое заблуждение - uniq ищет все повторяющиеся строки. На самом деле он удаляет только соседние дубликаты. Например: apple banana apple orange Если выполнить: uniq file.txt получим тот же самый список - одинаковые строки не стоят рядом. ▪️Зачем нужен sort После сортировки одинаковые строки оказываются соседними: sort file.txt | uniq Результат: apple banana orange Именно поэтому эти команды почти всегда используются вместе. ▪️Когда сортировка не подходит Если порядок строк важен, sort изменит его. Например, при анализе логов или событий по времени это может полностью исказить картину. В таких случаях лучше использовать: awk '!seen[$0]++' Команда удаляет дубликаты, но сохраняет исходный порядок строк. ▪️Найти только повторяющиеся строки Иногда нужны не уникальные записи, а наоборот - только дубликаты: sort file.txt | uniq -d Или вывести количество повторений: sort file.txt | uniq -c Получим: 15 nginx 8 sshd 3 cron ▪️Почему это важно uniq кажется простой утилитой, но ошибка в понимании её работы часто приводит к неверным результатам. Перед использованием всегда стоит помнить: она сравнивает только соседние строки, а не весь файл целиком. BashTex 📱 #sort
435
20
mkfifo: обмен данными между процессами через именованные каналы Обычный pipe: command1 | command2 работает только пока запущены обе команды. Но иногда нужен постоянный канал, к которому разные процессы могут подключаться позже. Для этого в Linux есть FIFO (named pipe). ▪️Создание именованного канала mkfifo /tmp/my_pipe Теперь /tmp/my_pipe выглядит как файл: ls -l /tmp/my_pipe Вывод: prw-r--r-- 1 user user 0 Aug 4 12:00 my_pipe Буква p означает pipe. ▪️Передача данных между двумя процессами Терминал 1: cat /tmp/my_pipe Терминал 2: echo "hello from process" > /tmp/my_pipe Первый процесс сразу получит сообщение. ▪️Пример с логированием Можно отправлять события из одного скрипта в другой: mkfifo /tmp/events.pipe tail -f /var/log/app.log > /tmp/events.pipe & А отдельный обработчик: while read line; do echo "EVENT: $line" done < /tmp/events.pipe Теперь обработка логов отделена от источника данных. ▪️Важный момент FIFO блокирует процесс: echo "data" > /tmp/my_pipe будет ждать, пока кто-то откроет канал на чтение. Поэтому в автоматизации часто используют фоновые процессы: cat /tmp/my_pipe & echo "start" > /tmp/my_pipe ▪️Чем отличается от обычного файла Данные в FIFO не сохраняются на диске. Если никто не читает: процесс → FIFO → процесс данные не складываются в очередь, а ждут получателя. ▪️Где полезно • связь между Bash-скриптами; • простые IPC-механизмы; • обработка потоков данных; • разделение генератора и обработчика. ▪️Почему это важно mkfifo часто заменяет временные файлы и сложные конструкции с перенаправлениями. Это простой способ построить связь между независимыми процессами прямо средствами Linux без дополнительных сервисов. BashTex 📱 #mfifo
468