BashTex | Linux
رفتن به کانال در Telegram
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin
نمایش بیشتر2 529
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+367 روز
+1930 روز
آرشیو پست ها
2 529
Когда переменные пропадают
Одна из частых ловушек в bash - переменная изменилась… но снаружи осталась прежней. Причина почти всегда одна - subshell.
▪️ Что такое subshell. Subshell - это дочерний процесс bash. Он получает копию переменных, но изменения не возвращаются назад. Создается, например, здесь:
( 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
Изменение потерялось.
▪️ Когда это полезно. Subshell - это не баг, а инструмент. Например:
( cd /tmp && ls )
внутри меняем каталог, а снаружи остаемся там же
BashTex 📱 #scripts2 529
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 📱 #bash #linux2 529
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 📱 #bash #linux2 529
exec: замена процесса без создания дочернего
Обычно при запуске команды Bash создаёт новый процесс:
sleep 60
После завершения sleep управление возвращается обратно в shell.
Но exec работает иначе - он заменяет текущий процесс новым. Старый процесс исчезает, а его PID сохраняется.
▪️Простой пример
echo $$
exec sleep 60
После выполнения вы больше не вернётесь в текущий shell. Процесс Bash будет полностью заменён на sleep.
▪️Почему PID не меняется
Запустим:
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 📱 #bash #linux2 529
readarray: читаем вывод команды в массив без циклов
Часто в Bash список файлов или строк читают через while read. Но если нужно просто получить все данные в массив, есть более короткий и быстрый способ - readarray (или его синоним mapfile).
▪️Классический подход
files=()
while IFS= read -r file; do
files+=("$file")
done < <(find /var/log -name "*.log")
Работает, но код получается довольно громоздким.
▪️То же самое через readarray
readarray -t files < <(
find /var/log -name "*.log"
)
Теперь весь вывод команды сразу окажется в массиве files.
▪️Что делает ключ -t
Без него каждый элемент массива будет содержать символ новой строки (\n) в конце.
Практически всегда стоит использовать:
readarray -t lines < file.txt
Так строки попадут в массив уже без лишних символов.
▪️Работа с массивом
Например, узнать количество найденных файлов:
echo "${#files[@]}"
Или пройтись по ним:
for file in "${files[@]}"; do
echo "$file"
done
▪️Когда использовать не стоит
readarray считывает весь поток целиком. Если команда выводит миллионы строк или очень большой файл, массив займёт соответствующий объём памяти.
В таких случаях потоковая обработка через while read остаётся более подходящим вариантом.
▪️Почему это важно
Во многих скриптах while read используется просто по привычке. Если задача - получить список строк для дальнейшей работы, readarray делает то же самое в одну команду, сохраняя код компактным и избавляя от ручного заполнения массива.
BashTex 📱 #bash #linux2 529
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 📱 #bash #linux2 529
🤖 Программирование — ВСЁ?
В 2026 году голым кодингом уже никого не удивишь - рутину за секунды набрасывают нейросети. Новая и прибыльная перспектива - разработка ИИ-агентов и автоматизация процессов.
Хватит тратить токены на генерацию картинок и базовых текстов, пора внедрять ИИ в реальный продакшн. Ловите канал, который тащит только отборное технологическое мясо:
👨💻 ИИнтеллигенция • Нейросети - канал для бэкендеров, автоматизаторов и инди-хакеров. Глубокие разборы ИИ-инструментов, промпт-инжиниринг и архитектура агентов без хайпа и копипасты.
⚡️ Разборы софта: от консольных плагинов вроде Claude SEO до платформ от NVIDIA.
⚡️ Экономика токенов: честные тесты, где Grok 4.5 или DeepSeek выгоднее, чем дорогие модели.
⚡️ Практика: как разворачивать бэкграунд-воркеров и автоматизировать рутину до одной кнопки.
⬇️ Вступай и качай навыки будущего: @clucai
2 529
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
}
level1
caller 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 📱 #bash #linux2 529
Проверка процессов с изменённым OOM Score:
/proc/*/oom_score_adj
Когда Linux заканчивается память, ядро запускает OOM Killer. Он выбирает процесс для завершения не случайно - решение зависит от внутренней оценки процесса.
Этой оценкой можно управлять через oom_score_adj.
▪️Посмотреть текущий OOM score процесса
У каждого процесса есть файлы в /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 📱 #bash #linux2 529
Проверка изменения списка загруженных модулей ядра:
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 📱 #bash #linux2 529
Поиск бинарников с Linux Capabilities:
getcap -r /
В Linux привилегии процесса не всегда завязаны только на root. Существуют capabilities - отдельные разрешения ядра, которые можно выдавать конкретным бинарникам.
Например, программе можно дать возможность менять сетевые настройки без полного root-доступа.
▪️Посмотреть capabilities у файла
getcap /usr/bin/pingПример вывода:
/usr/bin/ping cap_net_raw=ep
Это означает, что ping имеет право создавать raw-сокеты.
▪️Поиск всех бинарников с capabilities
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=ep
e (effective) — разрешение активно при запуске;
p (permitted) — процесс может его использовать.
Чаще всего встречается комбинация ep.
▪️Некоторые capabilities фактически дают очень широкие возможности:
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
Ядро хранит их в виде битовых масок.
▪️Удаление capability
Если разрешение больше не требуется:
setcap -r /path/to/binary
После этого файл вернётся к обычной модели прав.
▪️Почему это важно
При аудите Linux недостаточно искать только SUID-биты:
find / -perm -4000
Современные системы активно используют capabilities, и иногда один бинарник с лишним разрешением может иметь больше привилегий, чем ожидается.
BashTex 📱 #bash #linux2 529
Поиск процессов с удалённым исполняемым файлом
В 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 📱 #bash #linux2 529
Поиск файлов с расширенными ACL:
getfacl и find для выявления нестандартных прав
В Linux права доступа обычно проверяют через:
ls -l
Но этого недостаточно. Если у файла есть ACL (Access Control List), дополнительный пользователь или группа могут иметь доступ, которого не видно в стандартном выводе.
▪️Проверяем ACL у файла
getfacl /etc/passwd
Обычный вывод:
user::rw- group::r-- other::r--
Если есть дополнительные правила:
user::rw- user:backup:r-- group::r-- mask::r-- other::---
Здесь пользователь backup получил отдельное разрешение.
▪️Как найти файлы с ACL
Команда find умеет искать расширенные ACL:
find / -type f -acl 2>/dev/null
Но на многих системах удобнее использовать проверку через getfacl:
find /etc -type f -exec getfacl -p {} \; 2>/dev/null | grep "^user:"
▪️Проверка конкретного каталога
Например, ищем нестандартные права в /srv:
getfacl -R /srv
Обращаем внимание на строки:
user:name: group:name:
Они показывают дополнительные разрешения.
▪️Удаление лишних ACL
Если доступ больше не нужен:
setfacl -b file.txt
Ключ -b удаляет все дополнительные ACL, оставляя только обычные Unix-права.
▪️Почему ACL часто забывают
Администратор может изменить права:
chmod 600 secret.txt
и считать файл закрытым.
Но если раньше был добавлен ACL:
user:john:r--
пользователь John всё ещё может читать файл.
▪️Почему это важно
ACL полезны для сложных систем с большим количеством пользователей, но они усложняют аудит. При расследовании проблем с доступом всегда нужно проверять не только chmod, но и расширенные правила через getfacl.
BashTex 📱 #bash #linux2 529
Анализ активных UNIX-сокетов:
ss -xl и неожиданные IPC-соединения
Когда ищут открытые сервисы на сервере, обычно проверяют TCP и UDP:
ss -tulpn
Но часть процессов вообще не использует сеть. Они общаются через UNIX-сокеты - локальный механизм IPC между процессами.
▪️Посмотреть активные UNIX-сокеты
ss -x
Ключ -x включает UNIX-сокеты.
Чтобы увидеть только ожидающие подключения:
ss -xl
Пример:
u_str LISTEN 0 128 /run/docker.sock
Это означает, что какой-то процесс принимает локальные подключения через этот сокет.
▪️Найти владельца сокета
Добавляем информацию о процессе:
ss -xlp
Пример:
users:(("dockerd",pid=842,fd=7))
Теперь понятно, какой процесс создал точку обмена.
▪️Проверка прав доступа
UNIX-сокет - это файл, поэтому у него есть обычные права:
ls -l /run/docker.sock
Например:
srw-rw---- root docker docker.sock
Группа с правом записи может управлять Docker через этот сокет.
▪️Найти все сокеты на диске
find / -type s 2>/dev/null
Особое внимание:
/tmp
/dev/shm
/home/*
Системные сервисы чаще используют:
/run /var/run
▪️Посмотреть, кто держит конкретный сокет
Через lsof:
lsof /run/docker.sock
или:
fuser /run/docker.sock
▪️Почему это важно
Открытый TCP-порт виден сразу при сетевом сканировании. UNIX-сокеты остаются внутри системы и часто забываются при аудите.
При проверке сервера стоит смотреть не только наружные соединения, но и локальные каналы связи между процессами: именно там могут находиться Docker API, базы данных, агенты мониторинга и другие сервисы с важными правами доступа.
BashTex 📱 #bash #linux2 529
tee не только для логов: запись сразу в несколько потоков
Большинство используют tee только для сохранения вывода команды в файл:
command | tee output.log
Но возможности tee этим не ограничиваются. Он позволяет разветвлять поток данных и отправлять его сразу в несколько мест.
▪️Запись в несколько файлов
Например, сохранить один и тот же вывод сразу в два файла:
dmesg | tee kernel.log backup.log >/dev/null
Не нужно запускать команду дважды - tee сам продублирует поток.
▪️Логирование без потери вывода
Если скрипт должен писать лог, но при этом вывод оставаться в терминале:
./backup.sh | tee backup.log
Пользователь видит процесс выполнения, а лог сохраняется автоматически.
▪️Добавление вместо перезаписи
По умолчанию tee перезаписывает файл.
Чтобы дописывать:
echo "Started" | tee -a app.log
Ключ -a работает так же, как >>.
▪️Передача сразу в несколько команд
Вместе с process substitution можно построить разветвление потока:
journalctl -f \
| tee >(grep ERROR > errors.log) \
>(wc -l > count.txt) \
> /dev/null
Теперь один поток одновременно:
фильтруется по ошибкам;
подсчитывается;
может использоваться в других обработчиках.
Команда journalctl при этом запускается только один раз.
▪️Почему это важно
tee - это не просто инструмент для записи логов. Он позволяет строить конвейеры, где один источник данных обслуживает сразу несколько получателей. Это особенно полезно при анализе логов, мониторинге и автоматизации, когда дорого или невозможно повторно запускать исходную команду.
BashTex 📱 #bash #linux2 529
wait и wait -n: управление несколькими фоновыми задачами
Запустить процесс в фоне через & умеют почти все. Проблемы начинаются, когда таких процессов становится несколько и нужно понять, кто завершился и с каким кодом ошибки.
▪️Базовый wait
sleep 2 &
sleep 5 &
wait
echo "Все процессы завершились"
Без аргументов wait ждёт завершения всех фоновых задач текущего shell.
▪️Ожидание конкретного процесса
sleep 10 &
pid=$!
wait "$pid"
echo "Процесс завершён"
$! - PID последнего фонового процесса.
▪️Зачем нужен wait -n
Появился в Bash 4.3+ и решает важную задачу: ждать не всех, а первого завершившегося процесса.
sleep 5 &
sleep 2 &
sleep 8 &
wait -n
echo "Кто-то уже завершился"
Скрипт продолжит работу через 2 секунды, а не через 8.
▪️Практический сценарий
Параллельная обработка файлов:
for file in *.log; do
gzip "$file" &
done
while wait -n; do
echo "Одна задача завершилась"
done
Так можно реагировать на завершение задач по мере их выполнения, а не ждать самый медленный процесс.
▪️Почему это важно
Без wait Bash может завершить скрипт раньше фоновых процессов. А wait -n позволяет строить простые очереди задач и параллельную обработку без GNU Parallel, Python и других внешних инструментов. Для многих серверных скриптов этого уже достаточно.
BashTex 📱 #bash #linux2 529
Аудит переменной
$PATH: поиск небезопасных директорий и дубликатов
Переменная $PATH определяет, где shell ищет исполняемые файлы. Один лишний каталог - и вместо системной утилиты может запуститься совсем другая программа.
Именно поэтому $PATH стоит периодически проверять.
▪️Посмотреть порядок поиска
echo "$PATH" | tr ':' '\n'
Shell просматривает каталоги сверху вниз. Если команда встречается в нескольких местах, будет использована первая найденная.
▪️Поиск дубликатов
Повторяющиеся каталоги не ломают систему, но замедляют поиск команд и усложняют отладку:
echo "$PATH" | tr ':' '\n' | sort | uniq -d
Если вывод не пустой - есть дубликаты.
▪️Опасные записи
Особое внимание стоит обратить на:
.
./bin
/tmp
/var/tmp
/home/user/bin
Каталог . означает “текущая директория”.
Если он находится в $PATH, достаточно оказаться в папке с вредоносным файлом ls или ssh, чтобы по ошибке запустить его вместо системной утилиты.
▪️Проверка прав доступа
Даже “нормальный” каталог может быть проблемой, если в него могут писать другие пользователи:
find $(echo "$PATH" | tr ':' ' ') \
-maxdepth 0 -perm -0002 -ls 2>/dev/null
World-writable директории в $PATH - серьёзный повод для проверки.
▪️Какая команда будет запущена?
Не всегда очевидно, какой бинарник использует shell:
type -a python
или
which -a python
Это покажет все найденные версии в порядке поиска.
▪️Почему это важно
Ошибки в $PATH могут приводить не только к странному поведению скриптов, но и к компрометации системы. Несколько минут аудита помогут обнаружить небезопасные каталоги, лишние записи и потенциальную подмену исполняемых файлов ещё до того, как это станет проблемой.
BashTex 📱 #bash #linux2 529
shopt: малоизвестные настройки Bash, которые меняют поведение shell
Большинство пользователей знают про set -e или set -u, но в Bash есть ещё один мощный инструмент настройки - shopt.
С его помощью можно включать и отключать десятки дополнительных возможностей оболочки.
▪️Посмотреть доступные опции
shopt
Или только включённые:
shopt -s
▪️globstar - рекурсивный поиск без find
По умолчанию:
ls **/*.log
не сработает.
Включаем:
shopt -s globstar
Теперь:
ls **/*.log
найдёт все .log-файлы во вложенных каталогах.
▪️nullglob - если файлов нет
Без этой опции:
for file in *.log; do
echo "$file"
done
выведет:
*.log
Вместо пустого списка.
Исправляем:
shopt -s nullglob
Теперь цикл просто не выполнится.
▪️dotglob - учитывать скрытые файлы
По умолчанию * не включает файлы, начинающиеся с точки.
После:
shopt -s dotglob
они тоже попадут в результат.
▪️failglob - защита от опечаток
Если шаблон не совпал ни с одним файлом:
rm *.bak
с failglob Bash сразу сообщит об ошибке вместо передачи шаблона команде.
shopt -s failglob
Это помогает избежать неожиданных сценариев в автоматизации.
▪️Почему это важно
Несколько опций shopt способны заметно изменить поведение Bash без переписывания скриптов. Особенно полезны globstar, nullglob и failglob - они делают работу с шаблонами более удобной и предсказуемой.
BashTex 📱 #bash #linux2 529
xargs против while read: где быстрее и безопаснее
Обе конструкции позволяют обработать список файлов или строк, но работают они по-разному. Из-за этого одна может быть заметно быстрее, а другая - безопаснее.
▪️Когда выигрывает xargs
Допустим, нужно удалить все .log-файлы:
find . -name "*.log" | xargs rm
xargs собирает несколько аргументов и передаёт их одной команде, поэтому вместо сотен запусков rm будет всего несколько.
Для большого количества файлов разница в производительности может быть существенной.
▪️Но есть проблема
По умолчанию xargs разделяет вход по пробелам и переводам строки.
Если встретится файл:
my file.log
или
backup 2025.log
команда отработает некорректно.
Безопасный вариант:
find . -name "*.log" -print0 | xargs -0 rm
Здесь разделителем становится символ NULL, поэтому пробелы и спецсимволы больше не проблема.
▪️Когда лучше while read
Если над каждой строкой нужно выполнить несколько действий:
find . -name "*.log" -print0 |
while IFS= read -r -d '' file; do
echo "Deleting: $file"
rm "$file"
done
Такой код легче расширять: добавить проверки, логирование, условия или обработку ошибок.
▪️Что быстрее?
Для простого запуска одной команды обычно выигрывает xargs.
Для сложной логики, где каждая строка проходит несколько этапов обработки, удобнее использовать while read.
▪️Почему это важно
Многие используют xargs и while read как взаимозаменяемые инструменты. На практике выбор зависит не только от скорости, но и от формата входных данных. Если есть вероятность встретить пробелы, переносы строк или необычные символы в именах файлов, безопасные варианты - xargs -0 или while read -d ''.
BashTex 📱 #bash #linux