Поиск процессов с удалённым исполняемым файлом
В Linux процесс продолжает работать, даже если его исполняемый файл уже удалён с диска. Пока процесс не завершится, ядро держит файл открытым через файловый дескриптор.
Такие процессы стоит периодически искать - особенно после обновлений ПО, удаления пакетов или при аудите безопасности.
▪️ Быстрая проверка
Исполняемый файл каждого процесса доступен через
Например:
Это означает, что процесс продолжает использовать уже удалённый бинарник.
▪️ Более удобный вариант
С помощью
Команда покажет не только исполняемые файлы, но и удалённые библиотеки, логи и другие объекты, которые всё ещё удерживаются процессами.
▪️ Почему это происходит
Типичный сценарий:
Во время обновления старый бинарник удаляется и заменяется новым. Но уже запущенный процесс продолжает выполнять старую версию из памяти.
Пока сервис не будет перезапущен, он работает с удалённым файлом.
▪️ Когда это становится проблемой
Если удерживается исполняемый файл или библиотека - система использует устаревший код.
Если удерживается большой удалённый лог:
место на диске не освободится, пока процесс не закроет файл или не завершится.
▪️ Какие процессы требуют внимания
Не все записи
системные сервисы после обновлений;
процессы с удалёнными библиотеками (
файлы в
▪️ Почему это важно
Поиск процессов с
BashTex📱 #bash #linux
В Linux процесс продолжает работать, даже если его исполняемый файл уже удалён с диска. Пока процесс не завершится, ядро держит файл открытым через файловый дескриптор.
Такие процессы стоит периодически искать - особенно после обновлений ПО, удаления пакетов или при аудите безопасности.
Исполняемый файл каждого процесса доступен через
/proc/<PID>/exe:ls -l /proc/*/exe 2>/dev/null | grep '(deleted)'
Например:
/proc/2841/exe -> /usr/bin/python3.12 (deleted)
Это означает, что процесс продолжает использовать уже удалённый бинарник.
С помощью
lsof:lsof | grep '(deleted)'
Команда покажет не только исполняемые файлы, но и удалённые библиотеки, логи и другие объекты, которые всё ещё удерживаются процессами.
Типичный сценарий:
apt upgrade
Во время обновления старый бинарник удаляется и заменяется новым. Но уже запущенный процесс продолжает выполнять старую версию из памяти.
Пока сервис не будет перезапущен, он работает с удалённым файлом.
Если удерживается исполняемый файл или библиотека - система использует устаревший код.
Если удерживается большой удалённый лог:
/var/log/app.log (deleted)
место на диске не освободится, пока процесс не закроет файл или не завершится.
Не все записи
(deleted) опасны. В первую очередь стоит проверить:системные сервисы после обновлений;
процессы с удалёнными библиотеками (
.so);файлы в
/tmp или /dev/shm, которые продолжают использоваться после удаления.Поиск процессов с
(deleted) помогает обнаружить сервисы, которые давно требуют перезапуска, понять, почему не освобождается место на диске, и выявить подозрительные процессы, работающие с уже отсутствующими файлами.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Поиск бинарников с Linux Capabilities:
В Linux привилегии процесса не всегда завязаны только на
Например, программе можно дать возможность менять сетевые настройки без полного root-доступа.
▪️ Посмотреть capabilities у файла
Пример вывода:
Это означает, что
▪️ Поиск всех бинарников с capabilities
Команда рекурсивно проверяет файловую систему и показывает файлы с назначенными capabilities.
Пример:
▪️ Что означают флаги
Capability может иметь разные состояния:
Чаще всего встречается комбинация
▪️ Некоторые capabilities фактически дают очень широкие возможности:
Например:
•
•
•
▪️ Проверка конкретного процесса
У работающего процесса capabilities находятся здесь:
Ядро хранит их в виде битовых масок.
▪️ Удаление capability
Если разрешение больше не требуется:
После этого файл вернётся к обычной модели прав.
▪️ Почему это важно
При аудите Linux недостаточно искать только SUID-биты:
Современные системы активно используют capabilities, и иногда один бинарник с лишним разрешением может иметь больше привилегий, чем ожидается.
BashTex📱 #bash #linux
getcap -r /В Linux привилегии процесса не всегда завязаны только на
root. Существуют capabilities - отдельные разрешения ядра, которые можно выдавать конкретным бинарникам.Например, программе можно дать возможность менять сетевые настройки без полного root-доступа.
getcap /usr/bin/ping
Пример вывода:
/usr/bin/ping cap_net_raw=ep
Это означает, что
ping имеет право создавать raw-сокеты.getcap -r / 2>/dev/null
Команда рекурсивно проверяет файловую систему и показывает файлы с назначенными capabilities.
Пример:
/usr/bin/python3 cap_net_bind_service=ep
/usr/local/bin/app cap_sys_admin=ep
Capability может иметь разные состояния:
cap_net_raw=epe (effective) — разрешение активно при запуске;p (permitted) — процесс может его использовать.Чаще всего встречается комбинация
ep.cap_sys_admin
cap_dac_override
cap_setuid
cap_sys_ptrace
Например:
•
cap_dac_override позволяет обходить обычные проверки прав файлов;•
cap_setuid позволяет менять UID процесса;•
cap_sys_ptrace даёт возможность взаимодействовать с другими процессами.У работающего процесса capabilities находятся здесь:
cat /proc/1234/status | grep Cap
Ядро хранит их в виде битовых масок.
Если разрешение больше не требуется:
setcap -r /path/to/binary
После этого файл вернётся к обычной модели прав.
При аудите Linux недостаточно искать только SUID-биты:
find / -perm -4000
Современные системы активно используют capabilities, и иногда один бинарник с лишним разрешением может иметь больше привилегий, чем ожидается.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Проверка изменения списка загруженных модулей ядра:
Ядро Linux работает с модулями, которые можно загружать и выгружать без перезагрузки системы.
Это удобно для драйверов и расширений, но неожиданные изменения списка модулей могут быть важным сигналом при аудите.
▪️ Получить текущий список модулей
Пример:
Но для автоматического сравнения лучше сохранить только имена:
▪️ Создание базового снимка
После установки и настройки сервера можно сохранить эталон:
Позже сравнить:
▪️ Поиск новых модулей
Например:
Покажет только то, что появилось после создания снимка.
▪️ Проверка загрузки модулей в логах
Ядро пишет события загрузки:
Или:
▪️ Кто загрузил модуль?
Информацию о самом модуле можно получить через:
Покажет:
файл модуля;
автора;
версию;
зависимости.
▪️ Почему это важно
Большинство администраторов контролируют процессы и сетевые порты, но забывают про уровень ядра.
Неожиданный модуль может появиться после установки нового ПО, драйвера или изменения конфигурации. Сравнение снимков
BashTex📱 #bash #linux
lsmod + сравнение снимковЯдро Linux работает с модулями, которые можно загружать и выгружать без перезагрузки системы.
Это удобно для драйверов и расширений, но неожиданные изменения списка модулей могут быть важным сигналом при аудите.
lsmod
Пример:
Module Size Used by
nf_conntrack 176128 1
veth 32768 2
Но для автоматического сравнения лучше сохранить только имена:
lsmod | awk 'NR>1 {print $1}' | sort > modules.currentПосле установки и настройки сервера можно сохранить эталон:
lsmod | awk 'NR>1 {print $1}' | sort > /var/lib/modules.snapshotПозже сравнить:
diff /var/lib/modules.snapshot modules.current
Например:
comm -13 /var/lib/modules.snapshot modules.current
Покажет только то, что появилось после создания снимка.
Ядро пишет события загрузки:
journalctl -k | grep -i module
Или:
dmesg | grep -i module
Информацию о самом модуле можно получить через:
modinfo veth
Покажет:
файл модуля;
автора;
версию;
зависимости.
Большинство администраторов контролируют процессы и сетевые порты, но забывают про уровень ядра.
Неожиданный модуль может появиться после установки нового ПО, драйвера или изменения конфигурации. Сравнение снимков
lsmod помогает быстро увидеть изменения в состоянии ядра и понять, что именно изменилось на сервере.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
Проверка процессов с изменённым OOM Score:
Когда Linux заканчивается память, ядро запускает OOM Killer. Он выбирает процесс для завершения не случайно - решение зависит от внутренней оценки процесса.
Этой оценкой можно управлять через
▪️ Посмотреть текущий OOM score процесса
У каждого процесса есть файлы в
И значение корректировки:
▪️ Найти процессы с изменённым приоритетом
Если процесс имеет значение отличное от
▪️ Что означают значения
Например:
Процесс сложнее убить при OOM.
Процесс становится более вероятной жертвой.
Особое значение:
полностью запрещает OOM Killer выбирать этот процесс.
▪️ Посмотреть, кто занимает память и имеет высокий приоритет убийства
Получаем одновременно:
PID;
имя процесса;
потребление памяти;
настройку OOM.
▪️ Где это встречается
systemd может задавать параметр прямо в unit:
Также это часто используют контейнерные системы, чтобы защитить критичные процессы.
▪️ Почему это важно
Иногда при расследовании OOM кажется, что ядро “выбрало неправильный процесс”. Но причина может быть в изменённом
Аудит этого параметра помогает понять, почему один сервис пережил нехватку памяти, а другой был завершён.
BashTex📱 #bash #linux
/proc/*/oom_score_adjКогда Linux заканчивается память, ядро запускает OOM Killer. Он выбирает процесс для завершения не случайно - решение зависит от внутренней оценки процесса.
Этой оценкой можно управлять через
oom_score_adj.У каждого процесса есть файлы в
/proc:cat /proc/$(pidof nginx)/oom_score
И значение корректировки:
cat /proc/$(pidof nginx)/oom_score_adj
oom_score — итоговый рейтинг для OOM Killer.oom_score_adj — ручная поправка от -1000 до 1000.for f in /proc/[0-9]*/oom_score_adj; do
value=$(cat "$f" 2>/dev/null)
if [ "$value" != "0" ]; then
pid=${f#/proc/}
pid=${pid%/oom_score_adj}
echo "$pid: $value"
fi
done
Если процесс имеет значение отличное от
0, кто-то явно менял его приоритет.Например:
-500Процесс сложнее убить при OOM.
500Процесс становится более вероятной жертвой.
Особое значение:
-1000полностью запрещает OOM Killer выбирать этот процесс.
ps -eo pid,comm,rss,oom_score_adj --sort=-rss | head
Получаем одновременно:
PID;
имя процесса;
потребление памяти;
настройку OOM.
systemd может задавать параметр прямо в unit:
[Service]
OOMScoreAdjust=-500
Также это часто используют контейнерные системы, чтобы защитить критичные процессы.
Иногда при расследовании OOM кажется, что ядро “выбрало неправильный процесс”. Но причина может быть в изменённом
oom_score_adj.Аудит этого параметра помогает понять, почему один сервис пережил нехватку памяти, а другой был завершён.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
caller: определение места вызова функции в BashПри отладке больших Bash-скриптов часто недостаточно знать, какая функция упала. Нужно понять, откуда именно она была вызвана.
Для этого есть встроенная команда
caller.function test_func() {
caller
}
test_funcВывод:
5 mainЭто означает: функция была вызвана из 5-й строки функции
main (или основного скрипта).Например, создаём функцию логирования:
log_error() {
echo "Error from:"
caller
}
backup() {
log_error
}
backupТеперь при ошибке видно не только сообщение, но и источник вызова.
caller умеет подниматься вверх по стеку:function level3() {
caller 0
caller 1
}
function level2() {
level3
}
function level1() {
level2
}
level1caller 0 покажет текущий уровень, а caller 1 - предыдущий.die() {
echo "Failed at:"
caller
exit 1
}
deploy() {
false || die
}
deployВместо:
Failedполучаем:
Failed at:12 deployСразу понятно, где искать проблему.
$FUNCNAMEВ Bash уже есть массивы:
echo "${FUNCNAME[@]}"Они показывают цепочку функций:
deploy backup main
А
caller дополнительно даёт:номер строки;
имя файла;
контекст вызова.
большие Bash-проекты;
библиотеки функций;
CI/CD-скрипты;
автоматизация с большим количеством уровней вызова.
В маленьких скриптах ошибка обычно очевидна. Но когда Bash превращается в несколько сотен строк с десятками функций, трассировка вызовов становится такой же важной, как в обычных языках программирования.
caller - простой встроенный инструмент, который превращает отладку Bash из поиска по всему файлу в точное указание места проблемы.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
BashTex | Linux
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
👍8
hash: почему Bash иногда запускает “не ту” командуКаждый раз искать исполняемый файл в каталогах из
$PATH было бы дорого. Поэтому Bash запоминает найденные пути в собственном кэше команд.Из-за этого после обновления или перемещения бинарника можно столкнуться с неожиданным поведением.
hash
Пример вывода:
hits command
12 /usr/bin/git
5 /usr/bin/python3
8 /usr/bin/ssh
Здесь видно, какие команды Bash уже нашёл и закэшировал.
Допустим, Bash уже знает:
/usr/bin/python3
Но затем появился другой бинарник раньше в
$PATH:/usr/local/bin/python3
Shell может продолжить использовать старый путь, пока кэш не будет обновлён.
Полностью:
hash -r
После этого Bash снова выполнит поиск команды по
$PATH.Очистить только одну запись:
hash -d python3
При следующем запуске путь будет найден заново.
type -a python3
или
which -a python3
Это покажет все найденные бинарники, но именно Bash может использовать уже закэшированный путь.
Частые ситуации:
• установка новой версии программы в
/usr/local/bin;• изменение
$PATH внутри скрипта;• переключение между несколькими версиями Python, Java или Node.js;
• обновление исполняемых файлов без открытия нового терминала.
Если после изменения
$PATH или установки новой версии команда продолжает запускать старый бинарник, проблема может быть не в переменной окружения, а в кэше Bash.Одна команда
hash -r часто решает проблему быстрее, чем долгий поиск ошибок в конфигурации.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
readarray: читаем вывод команды в массив без цикловЧасто в Bash список файлов или строк читают через
while read. Но если нужно просто получить все данные в массив, есть более короткий и быстрый способ - readarray (или его синоним mapfile).files=()
while IFS= read -r file; do
files+=("$file")
done < <(find /var/log -name "*.log")
Работает, но код получается довольно громоздким.
readarrayreadarray -t files < <(
find /var/log -name "*.log"
)
Теперь весь вывод команды сразу окажется в массиве
files.-tБез него каждый элемент массива будет содержать символ новой строки (
\n) в конце.Практически всегда стоит использовать:
readarray -t lines < file.txt
Так строки попадут в массив уже без лишних символов.
Например, узнать количество найденных файлов:
echo "${#files[@]}"Или пройтись по ним:
for file in "${files[@]}"; do
echo "$file"
donereadarray считывает весь поток целиком. Если команда выводит миллионы строк или очень большой файл, массив займёт соответствующий объём памяти.В таких случаях потоковая обработка через
while read остаётся более подходящим вариантом.Во многих скриптах
while read используется просто по привычке. Если задача - получить список строк для дальнейшей работы, readarray делает то же самое в одну команду, сохраняя код компактным и избавляя от ручного заполнения массива.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
exec: замена процесса без создания дочернего
Обычно при запуске команды Bash создаёт новый процесс:
После завершения sleep управление возвращается обратно в shell.
Но exec работает иначе - он заменяет текущий процесс новым. Старый процесс исчезает, а его PID сохраняется.
▪️ Простой пример
После выполнения вы больше не вернётесь в текущий shell. Процесс Bash будет полностью заменён на sleep.
▪️ Почему PID не меняется
Запустим:
И снова:
PID останется тем же. Новый экземпляр Bash не был создан - текущий процесс просто заменился.
▪️ Где это действительно полезно
Представьте простой launcher:
После запуска в системе не останется лишнего процесса Bash - только myapp.
Это особенно важно для:
контейнеров Docker;
systemd-сервисов;
entrypoint-скриптов.
▪️ Почему это лучше обычного запуска
Без exec:
С exec:
myapp
Нет промежуточного процесса, сигналы (SIGTERM, SIGINT) сразу получает приложение, а не оболочка.
▪️ Когда использовать нельзя
После exec текущий скрипт прекращает существование:
Строка After никогда не выполнится.
▪️ Почему это важно
Многие воспринимают exec как ещё один способ запуска команды. На самом деле это механизм замены процесса. Он позволяет убрать лишний уровень в дереве процессов, избежать проблем с обработкой сигналов и сделать запуск сервисов более предсказуемым.
BashTex📱 #bash #linux
Обычно при запуске команды Bash создаёт новый процесс:
sleep 60
После завершения sleep управление возвращается обратно в shell.
Но exec работает иначе - он заменяет текущий процесс новым. Старый процесс исчезает, а его PID сохраняется.
echo $$
exec sleep 60
После выполнения вы больше не вернётесь в текущий shell. Процесс Bash будет полностью заменён на sleep.
Запустим:
echo $$
exec bash
И снова:
echo $$
PID останется тем же. Новый экземпляр Bash не был создан - текущий процесс просто заменился.
Представьте простой launcher:
#!/bin/bash
echo "Starting application..."
exec /usr/local/bin/myapp
После запуска в системе не останется лишнего процесса Bash - только myapp.
Это особенно важно для:
контейнеров Docker;
systemd-сервисов;
entrypoint-скриптов.
Без exec:
bash
└── myapp
С exec:
myapp
Нет промежуточного процесса, сигналы (SIGTERM, SIGINT) сразу получает приложение, а не оболочка.
После exec текущий скрипт прекращает существование:
echo "Before"
exec sleep 5
echo "After"
Строка After никогда не выполнится.
Многие воспринимают exec как ещё один способ запуска команды. На самом деле это механизм замены процесса. Он позволяет убрать лишний уровень в дереве процессов, избежать проблем с обработкой сигналов и сделать запуск сервисов более предсказуемым.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍1🗿1
find -exec против -exec {} +: почему одна команда может быть в десятки раз быстрееМногие используют
find так:find /var/log -name "*.log" -exec gzip {} \;Команда работает, но есть нюанс: для каждого найденного файла запускается отдельный процесс
gzip.Если найдено 5000 файлов, Bash выполнит примерно 5000 запусков
gzip.Это лишние создания процессов, переключения контекста и заметная потеря производительности.
find /var/log -name "*.log" -exec gzip {} +Теперь
find собирает несколько файлов и передаёт их одной команде:gzip file1.log file2.log file3.log ...
Количество запусков сокращается в десятки или даже сотни раз.
Вариант с
\;:gzip file1
gzip file2
gzip file3
Вариант с
+:gzip file1 file2 file3
Логика обработки не меняется, но накладные расходы становятся значительно меньше.
+ использовать нельзяЕсли команда должна выполняться отдельно для каждого файла, например:
find . -type f -exec mv {} {}.bak \;или внутри используется сложная оболочка:
find . -type f -exec sh -c '...' \;
В таких случаях объединение аргументов может изменить поведение команды.
Используйте
-exec {} +, когда команда умеет принимать сразу несколько файлов:rm
chmod
chown
gzip
cp
mv (при копировании в каталог)
ls
Если же обработка должна происходить строго по одному файлу — остаётся
-exec {} \;.Во многих скриптах
find -exec {} \; используется по привычке. Замена всего одного символа — \; на + - позволяет значительно сократить число создаваемых процессов и ускорить обработку больших каталогов без изменения логики скрипта.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
env -i: запуск программы в полностью “чистом” окруженииБольшинство программ наследуют десятки переменных окружения:
PATH, HOME, LANG, LD_LIBRARY_PATH, прокси, токены и многое другое.Иногда именно они становятся причиной странного поведения приложения.
env
или:
printenv
Вы увидите все переменные, которые будут унаследованы дочерними процессами.
Команда:
env -i bash
запустит новый Bash практически без переменных окружения.
Если выполнить:
envвывод окажется пустым или почти пустым.
Например:
env -i PATH=/usr/bin:/bin HOME=/tmp bash
Теперь программа получит только
PATH и HOME.Это особенно удобно при поиске ошибок.
Есть скрипт:
./deploy.sh
У одного пользователя он работает, у другого - нет.
Проверяем:
env -i PATH=/usr/bin:/bin ./deploy.sh
Если проблема исчезла или, наоборот, воспроизвелась, причина, скорее всего, в одной из переменных окружения, а не в самом скрипте.
• отладка CI/CD;
• проверка зависимостей от окружения;
• запуск сервисов в предсказуемых условиях;
• поиск проблем с
PATH, LANG, HOME и прокси.Ошибки, связанные с окружением, часто трудно воспроизвести: скрипт работает у одного администратора и ломается у другого.
env -i позволяет полностью исключить влияние наследуемых переменных и быстро понять, зависит ли программа от окружения больше, чем кажется.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
Forwarded from localhost
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7
Когда переменные пропадают
Одна из частых ловушек в bash - переменная изменилась… но снаружи осталась прежней. Причина почти всегда одна - subshell.
▪️ Что такое subshell. Subshell - это дочерний процесс bash. Он получает копию переменных, но изменения не возвращаются назад. Создается, например, здесь:
или в пайпах:
▪️ Классическая проблема
Результат: 0
Почему? Цикл while выполняется в subshell.
▪️ Правильный вариант
Результат: 3
Теперь цикл работает в текущем shell.
▪️ Ещё пример
Результат: 1
Изменение потерялось.
▪️ Когда это полезно. Subshell - это не баг, а инструмент. Например:
внутри меняем каталог, а снаружи остаемся там же
BashTex📱 #scripts
Одна из частых ловушек в bash - переменная изменилась… но снаружи осталась прежней. Причина почти всегда одна - subshell.
( command )
или в пайпах:
command | while read ...
count=0
echo -e "a\nb\nc" | while read -r line; do
((count++))
done
echo "$count"
Результат: 0
Почему? Цикл while выполняется в subshell.
count=0
while read -r line; do
((count++))
done < <(echo -e "a\nb\nc")
echo "$count"
Результат: 3
Теперь цикл работает в текущем shell.
x=1
( x=5 )
echo "$x"
Результат: 1
Изменение потерялось.
( cd /tmp && ls )
внутри меняем каталог, а снаружи остаемся там же
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Почему
Когда нужно убрать дубликаты, многие сразу пишут:
Работает. Но только если понимать, что именно делает каждая команда.
▪️ Как работает
Распространённое заблуждение -
На самом деле он удаляет только соседние дубликаты.
Например:
Если выполнить:
получим тот же самый список - одинаковые строки не стоят рядом.
▪️ Зачем нужен
После сортировки одинаковые строки оказываются соседними:
Результат:
Именно поэтому эти команды почти всегда используются вместе.
▪️ Когда сортировка не подходит
Если порядок строк важен,
Например, при анализе логов или событий по времени это может полностью исказить картину.
В таких случаях лучше использовать:
Команда удаляет дубликаты, но сохраняет исходный порядок строк.
▪️ Найти только повторяющиеся строки
Иногда нужны не уникальные записи, а наоборот - только дубликаты:
Или вывести количество повторений:
Получим:
▪️ Почему это важно
BashTex📱 #sort
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Проверка доступности локальных Unix-сервисов:
Не все сервисы в Linux слушают TCP-порты. Многие приложения работают только через UNIX-сокеты - локальные каналы связи между процессами.
Например:
Снаружи такие сервисы не видны через обычный
▪️ Найти активные UNIX-сокеты
Пример:
Теперь можно проверить, отвечает ли сервис.
▪️ Проверка через
Netcat умеет подключаться к UNIX-сокетам:
Если соединение установлено - сокет доступен.
Для HTTP-сервисов можно отправить запрос вручную:
▪️ Проверка HTTP API через
Многие локальные API работают через UNIX-сокеты:
Здесь
▪️ Пример с Docker
Вместо:
можно напрямую обратиться к API:
Ответ:
означает, что Docker daemon доступен.
▪️ Проверка прав доступа
Даже если сервис работает, доступ может быть ограничен:
Например:
Только пользователи группы
▪️ Почему это важно
UNIX-сокеты часто остаются вне стандартного мониторинга. Администратор проверяет открытые порты, но забывает про локальные API и IPC-каналы.
Проверка через
жив ли сервис;
кто имеет к нему доступ;
отвечает ли локальное API;
нет ли неожиданного открытого канала между процессами.
BashTex📱 #sort
nc -U и curl --unix-socketНе все сервисы в Linux слушают TCP-порты. Многие приложения работают только через UNIX-сокеты - локальные каналы связи между процессами.
Например:
/run/docker.sock
/run/php/php-fpm.sock
/run/containerd/containerd.sock
Снаружи такие сервисы не видны через обычный
nmap, но внутри системы они могут иметь важное значение.ss -xl
Пример:
u_str LISTEN 0 128 /run/docker.sock
Теперь можно проверить, отвечает ли сервис.
ncNetcat умеет подключаться к UNIX-сокетам:
nc -U /run/docker.sock
Если соединение установлено - сокет доступен.
Для HTTP-сервисов можно отправить запрос вручную:
printf "GET / HTTP/1.0\r\n\r\n" | nc -U /run/service.sock
curlМногие локальные API работают через UNIX-сокеты:
curl --unix-socket /run/docker.sock http://localhost/version
Здесь
localhost не используется как сетевой адрес - он нужен только для формирования HTTP-запроса.Вместо:
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
eval: почему опасен и когда реально нуженВ Bash почти любую строку можно превратить в команду через
eval. Он берёт переданный текст и запускает его как новый Bash-код.Именно поэтому это один из самых спорных инструментов в shell-скриптах.
evalПростой пример:
cmd="ls -la"
eval "$cmd"
Bash сначала получает строку:
ls -la
а затем выполняет её как обычную команду.
Если данные приходят от пользователя:
read inputeval "$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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Проверка времени отклика сервисов
Когда сервис работает, но пользователи жалуются на медлительность, нужно мерить не аптайм, а отклик. Это легко сделать обычным curl.
▪️ Самый простой замер
Показывает общее время выполнения запроса и быстро понять, тормозит или нет.
▪️ Точнее: только сетевое время
Полезные метрики:
Пример:
▪️ Проверка нескольких сервисов
▪️ Таймаут обязателен
Без таймаута любой мониторинг бесполезен.
BashTex📱 #bash #utils
Когда сервис работает, но пользователи жалуются на медлительность, нужно мерить не аптайм, а отклик. Это легко сделать обычным curl.
time curl -s https://bashtex.com > /dev/null
Показывает общее время выполнения запроса и быстро понять, тормозит или нет.
curl -s -o /dev/null -w "time_total: %{time_total}\n" https://bashtex.com
Полезные метрики:
time_namelookuptime_connecttime_starttransfertime_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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👨💻4
/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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥1
/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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1