BashTex | Linux
前往频道在 Telegram
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin
显示更多2 518
订阅者
-324 小时
-97 天
-130 天
帖子存档
2 518
Проверка недавно созданных пользователей: UID, shell и следы активности
На сервере часто важно быстро понять, появлялись ли новые пользователи и насколько они “нормальные”.
Один из базовых источников -
/etc/passwd, но там нет явной даты создания. Поэтому анализ строится через косвенные признаки.
▪️Поиск пользователей с обычным UID диапазоном
getent passwd | awk -F: '$3 >= 1000 {print $1, $3, $7}'
Обычно реальные пользователи находятся начиная с UID 1000.
Системные - ниже.
Смотрим shell: /bin/bash, /bin/zsh чаще у людей, /usr/sbin/nologin или /bin/false - сервисные аккаунты.
▪️Быстрый срез через lastlog
lastlog -t 30
Показывает пользователей, которые логинились за последние 30 дней. Новые аккаунты без активности сразу заметны.
▪️Проверка событий создания пользователей
journalctl _COMM=useradd --since "7 days ago"
или через auth лог:
grep useradd /var/log/auth.log
Здесь видны факты создания аккаунтов и изменения групп.
▪️Косвенная проверка “новизны”
Можно оценить свежесть через системные изменения:
stat /etc/passwd
или изменения shadow:
stat /etc/shadow
Если недавно был добавлен пользователь, эти файлы будут обновлены в тот же период.
▪️Дополнительная проверка активности
faillog -a
и
last
Позволяют понять, пытался ли пользователь входить в систему и с каких хостов.
▪️Почему это важно
Новые пользователи с UID 1000+, нестандартным shell или отсутствием логинов — один из первых сигналов, который стоит проверять при аудите сервера.
BashTex 📱 #scripts #du2 518
Почему
kill -9 - не лучший способ остановить процесс
Многие администраторы при зависшем процессе сразу используют:
kill -9 <PID>
Обычно это работает. Но далеко не всегда это правильное решение.
▪️Что делает SIGKILL
Сигнал -9 (SIGKILL) принудительно завершает процесс на уровне ядра.
Процесс не может его перехватить, обработать или проигнорировать.
Ядро просто убивает его.
▪️Что при этом не происходит
Процесс не успевает:
сохранить данные
закрыть файлы
завершить активные операции
удалить временные файлы
выполнить обработчики завершения
Например, база данных может не успеть корректно завершить транзакции.
▪️Что использовать сначала
Обычный сигнал завершения:
kill <PID>
или
kill -15 <PID>
Это SIGTERM.
Процесс получает запрос на завершение и может корректно освободить ресурсы.
▪️Посмотреть, реагирует ли процесс
Отправляем SIGTERM:
kill -15 1234
Проверяем:
ps -p 1234
Если процесс всё ещё работает спустя разумное время, тогда можно переходить к более жёстким мерам.
▪️Полезный приём
Для сервисов под systemd лучше использовать:
systemctl stop myapp
systemd сам отправит нужные сигналы в правильном порядке и подождёт завершения процесса.
Только если сервис не реагирует, будет применено принудительное завершение.
▪️Почему это важно
kill -9 часто помогает быстро решить проблему, но одновременно может создавать новые. Поэтому его лучше рассматривать как последний инструмент, когда процесс уже не отвечает на обычное завершение.
BashTex 📱 #scripts #kill92 518
Почему
du и df показывают разный размер
Одна из классических загадок Linux:
df -h
Показывает:
/dev/sda1 100%
Но если проверить содержимое файловой системы:
du -sh /var du -sh /home du -sh /opt
Суммарный объём получается заметно меньше.
Куда пропало место?
▪️Что считают du и df
du подсчитывает размер файлов, которые видны в файловой системе.
df показывает занятые блоки на уровне самой файловой системы.
Из-за этого их значения могут различаться.
▪️Самая частая причина
Удалённый файл всё ещё открыт процессом.
Например:
rm app.log
Файл исчез из каталога, поэтому du его больше не видит.
Но если процесс продолжает писать в него, место останется занятым.
Найти такие файлы можно так:
lsof +L1
Пример вывода:
java 1234 user 5w REG ... app.log (deleted)
Пока процесс не завершится или не закроет дескриптор, место не освободится.
▪️Другие причины
Зарезервированные блоки ext4:
tune2fs -l /dev/sda1 | grep Reserved
Смонтированные файловые системы внутри каталогов:
mount | column -t
Или bind-монтирования, из-за которых данные учитываются неожиданным образом.
▪️Быстрая диагностика
Если диск внезапно заполнился:
df -h lsof +L1
Во многих случаях проблема обнаруживается уже на втором шаге.
▪️Почему это важно
На продакшн-серверах часто удаляют огромный лог в надежде освободить место. Но если процесс продолжает держать файл открытым, свободное пространство не появится, а приложение может вскоре упасть из-за нехватки диска.
BashTex 📱 #scripts #du2 518
tmpfiles.d: автоматическое создание и очистка директорий без cron
На многих серверах можно встретить скрипты, которые создают нужные каталоги при загрузке системы или периодически чистят временные файлы через cron.
Хотя systemd умеет делать это самостоятельно.
▪️Что такое tmpfiles.d
Механизм
tmpfiles.d позволяет создавать каталоги, менять права доступа, владельцев и автоматически удалять старые файлы по заданным правилам.
Конфигурации обычно находятся здесь:
/etc/tmpfiles.d/ /usr/lib/tmpfiles.d/
▪️Создание каталога при старте
Допустим, приложению нужен каталог для кэша:
d /var/cache/myapp 0755 myapp myapp -
Где:
d — создать директорию
0755 — права доступа
myapp myapp — владелец и группа
После применения:
systemd-tmpfiles --create
Каталог появится автоматически.
▪️Автоматическая очистка
Удалять файлы старше 7 дней:
D /var/tmp/myapp 0755 myapp myapp 7d
Теперь systemd будет очищать содержимое каталога согласно политике хранения.
Проверить правила можно так:
systemd-tmpfiles --clean
▪️Где это полезно
Кэш приложений, временные выгрузки, очереди обработки файлов, каталоги с отчётами и любые данные, которые должны жить ограниченное время.
▪️Почему это удобнее cron
Вместо отдельных скриптов и задач в планировщике вся логика хранения описывается одной строкой конфигурации. Создание, права доступа и очистка управляются стандартными средствами системы.
BashTex 📱 #scripts #tmpfiles2 518
Почему журналы journald могут неожиданно заполнить диск
На сервере закончилось место, а в
/var/log ничего подозрительного нет?
Часто виновником оказывается journald.
▪️Где хранятся логи
Проверить объём журналов можно одной командой:
journalctl --disk-usage
Например:
Archived and active journals take up 2.8G on disk.
На серверах с большим количеством сервисов объём может расти довольно быстро.
▪️Посмотреть самые “тяжёлые” журналы
Узнать, сколько данных записывается за последние сутки:
journalctl --since yesterday | wc -l
А если какой-то сервис генерирует тысячи сообщений в минуту:
journalctl -u myapp.service
Обычно проблема находится довольно быстро.
▪️Как очистить старые журналы
Оставить только последние 500 МБ:
journalctl --vacuum-size=500M
Или удалить записи старше 14 дней:
journalctl --vacuum-time=14d
Очистка происходит без остановки journald.
▪️Ограничение роста журналов
Настройки находятся в:
/etc/systemd/journald.conf
Например:
SystemMaxUse=1G SystemKeepFree=2G
После изменения конфигурации:
systemctl restart systemd-journald
▪️Почему это важно
Когда приложение начинает писать ошибки в цикле, journald может вырасти до нескольких гигабайт за считанные часы. Если заранее не настроить лимиты, одна неудачная конфигурация способна оставить сервер без свободного места.
BashTex 📱 #scripts #systemd2 518
PIPESTATUS: как узнать, где именно сломался пайп
Большинство администраторов знают про
$?, но при работе с пайпами он может вводить в заблуждение.
Посмотрим на пример:
grep ERROR app.log | sort | uniq
echo $?
Если пайп состоит из нескольких команд, $? покажет код возврата только последней из них.
Если grep завершился с ошибкой, а uniq отработал успешно, вы увидите:
0
Хотя одна из команд фактически провалилась.
▪️Решение - PIPESTATUS
После выполнения пайпа Bash сохраняет коды возврата всех его команд в массиве PIPESTATUS:
grep ERROR app.log | sort | uniq
echo "${PIPESTATUS[@]}"
Результат может выглядеть так:
2 0 0
Здесь видно, что ошибка произошла именно в grep.
▪️Проверка конкретного этапа
Можно обратиться к нужному элементу массива:
grep ERROR app.log | sort | uniq
echo "${PIPESTATUS[0]}"
Проверяем первую команду в цепочке.
echo "${PIPESTATUS[1]}"
Проверяем вторую.
▪️А что насчёт pipefail?
Многие включают:
set -o pipefail
Это полезно — теперь пайп вернёт ошибку, если упала любая команда внутри него.
Но pipefail не показывает, какой именно этап завершился неудачно.
Для диагностики всё равно пригодится PIPESTATUS.
▪️Почему это важно
Если в скрипте есть длинные цепочки из grep, awk, sed, jq, sort и других утилит, обычный $? может скрыть проблему. PIPESTATUS позволяет быстро понять, где именно произошёл сбой.
BashTex 📱 #scripts #pipestatus2 518
flock: защита от повторного запуска скрипта
Одна из самых неприятных проблем в автоматизации - случайный запуск нескольких копий одного скрипта.
Например, cron запускает задачу каждые 5 минут, но одна из прошлых итераций ещё не завершилась.
В результате появляются дублирующиеся процессы, конфликты при работе с файлами и непредсказуемые ошибки.
▪️Типичная ошибка
Допустим, скрипт выполняется 10 минут, а cron запускает его каждые 5:
*/5 * * * * /opt/backup.sh
Через некоторое время одновременно будут работать сразу несколько экземпляров.
▪️Решение через flock
Самый простой вариант - добавить блокировку:
flock -n /tmp/backup.lock /opt/backup.sh
Если lock-файл уже занят другим процессом, новая копия просто не запустится.
Ключ -n означает “не ждать освобождения блокировки”.
▪️Использование внутри скрипта
Можно защитить сам скрипт независимо от способа запуска:
exec 200>/var/run/myjob.lock
flock -n 200 || {
echo "Already running"
exit 1
}
Теперь второй экземпляр завершится сразу после старта.
▪️Чем лучше PID-файлов
Многие делают так:
echo $$ > /tmp/script.pid
Но после аварийного завершения PID-файл может остаться, а PID уже будет принадлежать другому процессу.
flock работает через файловые дескрипторы ядра и автоматически освобождает блокировку при завершении процесса.
▪️Где особенно полезно
Бэкапы, синхронизация файлов, обработка очередей, обновление данных, cron-задачи и любые скрипты, которые нельзя запускать параллельно.
BashTex 📱 #scripts #flock2 518
systemd.path: запуск действия при изменении файла
Многие задачи автоматизации до сих пор решают через бесконечные циклы:
while true; do
if [ -f /tmp/reload ]; then
systemctl restart myapp
rm /tmp/reload
fi
sleep 5
done
Работает, но процесс постоянно висит в памяти и каждые несколько секунд проверяет состояние файловой системы.
▪️ Что есть лучше
В systemd существует специальный тип юнитов — .path.
Он использует inotify и реагирует на реальные изменения файлов или директорий, а не занимается постоянным опросом.
Пример: нужно перезапускать сервис после изменения конфигурации.
Создаем path-юнит:
[Path] PathModified=/etc/myapp/config.yml [Install] WantedBy=multi-user.target
И связанный сервис:
[Service]
Type=oneshot
ExecStart=/bin/systemctl restart myapp.service
Активируем:
systemctl daemon-reload
systemctl enable --now myapp.path
Теперь изменение файла автоматически вызовет запуск сервиса.
▪️ Что умеет отслеживать
Не только изменение файла:
PathExists=/tmp/file PathChanged=/var/log/app.log DirectoryNotEmpty=/var/spool/tasks
Например, можно запускать обработчик сразу после появления нового файла в директории или при заполнении очереди задач.
▪️ Почему это удобно
Вместо собственных демонов, cron и циклов с sleep получаем событийную модель: пока ничего не происходит - не расходуются ресурсы. Как только происходит нужное событие - systemd запускает действие.
BashTex 📱 #bash2 518
Скрипт проверки зависших mount
Иногда сервер начинает странно тормозить: команда
df -h зависает, ls не отвечает, а проблема оказывается в примонтированной NFS/SMB/сетевой ФС. Причина простая: mount есть, но I/O к нему не отвечает.
Проверить это можно через связку: timeout + stat. Можно выполнить stat для точки монтирования, но ограничить время ожидания.
▪️ Пример ручной проверки
timeout 3 stat /mnt/backup
Если ФС отвечает - увидим информацию о каталоге. Если команда зависла и была остановлена по таймауту - mount подозрительный.
🛠 Скрипт проверки всех mount
#!/usr/bin/env bash
TIMEOUT=3
findmnt -rn -o TARGET | while read -r mountpoint; do
if timeout "$TIMEOUT" stat "$mountpoint" >/dev/null 2>&1; then
echo "OK: $mountpoint"
else
echo "HANG: $mountpoint"
fi
done
Сохраняем:
nano check-mounts.sh
Делаем исполняемым:
chmod +x check-mounts.sh
Запускаем:
sudo ./check-mounts.sh
Пример вывода
OK: /
OK: /boot
OK: /home
HANG: /mnt/backup
HANG: /mnt/nfs-share
🛠 Также можно проверять только сетевые ФС
#!/usr/bin/env bash
TIMEOUT=3
findmnt -rn -t nfs,nfs4,cifs,smb3 -o TARGET | while read -r mountpoint; do
if timeout "$TIMEOUT" stat "$mountpoint" >/dev/null 2>&1; then
echo "OK: $mountpoint"
else
echo "HANG: $mountpoint"
fi
done
Такой вариант удобнее для серверов, где много локальных mount.
⚠️ stat может зависнуть, если ядро ждет ответ от удаленного хранилища. Поэтому мы оборачиваем его в timeout. Без timeout ваш скрипт сам может зависнуть на проблемной точке монтирования.
▪️ Быстрая проверка одной строкой
findmnt -rn -o TARGET | while read -r mp; do
timeout 3 stat "$mp" >/dev/null 2>&1 \
&& echo "OK: $mp" \
|| echo "HANG: $mp"
done
BashTex 📱 #mount #script2 518
Как найти процессы, которые активно пишут на диск
1️⃣ iotop - самый быстрый способ
sudo iotop -o
Ключ -o показывает только процессы с активным I/O.
Для вывода без интерактивного режима:
sudo iotop -o -b -n 5
-b - batch mode
-n 5 - 5 обновлений и выход
Смотрите на колонки: DISK WRITE и COMMAND
2️⃣ pidstat - запись по процессам
pidstat -d 1
Показывает дисковую активность процессов каждую секунду. Главная колонка: kB_wr/s. Она показывает скорость записи на диск. Пример:
PID kB_rd/s kB_wr/s Command
1245 0.00 820.00 rsyslogd
2178 0.00 4096.00 mysqld
3️⃣ Проверка через /proc. У каждого процесса есть файл:
/proc/<PID>/io
Посмотреть статистику:
sudo cat /proc/<PID>/io
Ищем строку: write_bytes: Она показывает, сколько байт процесс записал с момента запуска.
Быстрый топ процессов по записи:
for pid in /proc/[0-9]*; do
[[ -r "$pid/io" ]] || continue
wb=$(awk '/write_bytes/ {print $2}' "$pid/io" 2>/dev/null)
cmd=$(cat "$pid/comm" 2>/dev/null)
[[ "$wb" -gt 0 ]] && echo "$wb PID=${pid##*/} $cmd"
done | sort -nr | head
4️⃣ Какие файлы пишет процесс
sudo lsof -p <PID>
Например:
sudo lsof -p 2178
Так можно понять, какие файлы открыты у подозрительного процесса.
5️⃣ Небольшая итоговая шпаргалка
sudo iotop -o
Кто пишет на диск прямо сейчас.
pidstat -d 1
Скорость чтения/записи по процессам.
sudo cat /proc/<PID>/io
I/O-статистика процесса.
sudo lsof -p <PID>
Какие файлы открыл процесс.
BashTex 📱 #storage2 518
Почему while read ломается в пайпах
Классическая ловушка Bash:
count=0
cat file.txt | while read -r line; do
((count++))
done
echo "$count"
Ожидание: увидим количество строк.
Реальность: 0.
▪️ Что произошло
Когда while read стоит справа от пайпа, цикл часто выполняется в subshell - дочернем процессе. То есть переменная count меняется не в текущем shell, а в его копии. После окончания цикла изменения исчезают.
▪️ Как делать правильно. Вариант 1: редирект файла вместо pipe
count=0
while IFS= read -r line; do
((count++))
done < file.txt
echo "$count"
Теперь цикл работает в текущем shell, и count сохраняется.
Вариант 2: process substitution. Если источник - команда, а не файл:
count=0
while IFS= read -r line; do
((count++))
done < <(grep ERROR app.log)
echo "$count"
<(...) подставляет вывод команды как файл, без subshell-проблемы в самом цикле.
▪️ Почему это важно
Ломаются не только счетчики. Точно так же исчезают:, массивы, флаги, накопленные строки и любые переменные, измененные внутри цикла
BashTex 📱 #bash2 518
Файлы удалены, а место не освободилось
Классическая ситуация: удалили большой лог, df все еще показывает, что диск забит.
Ответ: файл удален из файловой системы, но процесс всё ещё держит его открытым.
▪️ Почему так происходит
В linux файл удаляется не сразу. Если процесс держит дескриптор: файл исчезает из каталога, но данные остаются на диске пока процесс не закроет файл
▪️ Как найти такие файлы
lsof | grep deleted
Пример вывода:
nginx 1234 user 10w REG ... /var/log/nginx/access.log (deleted)
Это значит: файл удален, но процесс nginx все еще его держит
▪️ Найти самые тяжелые
lsof -nP | grep deleted | sort -k7 -nr | head
Так можно быстро увидеть, кто реально съедает место.
▪️ Что делать
Вариант 1 - перезапустить сервис
systemctl restart nginx
Процесс закроет файл и место освободится.
Вариант 2 - убить процесс
kill 1234
(если можно безопасно остановить)
Вариант 3 - обнулить файл через
/proc
: > /proc/1234/fd/10
Где:
1234 - PID
10 - дескриптор из lsof
Это освободит место без перезапуска процесса.
BashTex 📱 #storage2 518
Когда переменные пропадают
Одна из частых ловушек в 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 518
Подготовка к DevOps/SRE интервью с «Troubleshooting Docker и Kubernetes: поиск и устранение проблем»
В программе только важные аспекты:
— troubleshooting Docker и образов
— диагностика сетевых проблем
— настройка readiness/liveness probes
— отладка pod’ов, деплоев и ingress
— анализ логов контейнеров и кластера
— разбор ошибок CrashLoopBackOff, OOMKilled, ImagePullBackOff и других
Собеседования на DevOps/SRE сейчас всё чаще строятся вокруг реальных инцидентов. Данный курс фокусируется именно на таких сценариях и помогает в подготовке к практическим вопросам
48 часов доступен со скидкой 25%
↗️ Пройти курс на Stepik
2 518
Работа с сигналами: SIGTERM, SIGINT, SIGHUP
Любой процесс в linux можно остановить сигналом. Если скрипт их не обрабатывает - он просто умирает, иногда оставляя: временные файлы, lock-файлы или незавершённые операции
▪️ Основные сигналы
SIGINT - (2) -
Ctrl+C
Остановка пользователем
SIGTERM (15) - вежливое завершение - systemctl stop / kill
Дает процессу шанс корректно завершиться.
SIGHUP (1) - перечитай конфиг
часто используется для reload
▪️ Обработка сигналов через trap
trap 'echo "Получен SIGINT"; cleanup; exit 1' INT
trap 'echo "Получен SIGTERM"; cleanup; exit 0' TERM
Теперь скрипт не просто падает, а выполняет cleanup.
▪️ Пример cleanup
cleanup() {
echo "Чистка..."
rm -f /tmp/my.lock
}
▪️ Полный пример
#!/usr/bin/env bash
LOCK=/tmp/job.lock
touch "$LOCK"
cleanup() {
echo "Удаляю lock"
rm -f "$LOCK"
}
trap cleanup EXIT
trap 'echo "SIGINT"; exit 1' INT
trap 'echo "SIGTERM"; exit 0' TERM
while true; do
echo "Работаю..."
sleep 2
done
Теперь:
Ctrl+C - корректное завершение
kill - cleanup выполняется
скрипт не оставляет мусор
Не все сигналы можно перехватить. Нельзя поймать: SIGKILL (9)
Если прилетит kill -9 - все, без cleanup.
BashTex 📱 #scripts #utils2 518
🔈 Новый VPS/VDS-сервис от упоротых инфраструктурщиков
Давно дружим с CORTEL, это ребята про enterprise решения для крупняка. Сейчас они запустили отдельный бренд для аренды VPS/VDS — Serverum.
Это сервис, где можно выбрать VPS, оплатить и сразу начать пользоваться. Подойдёт для dev/stage-сред, тестовых стендов, ботов, pet-проектов, небольших сервисов и других задач, где нужен сервер без лишней возни.
Внутри:
— собственная проприетарная платформа
— отечественные решения
— защищённая инфраструктура
— низкие цены
— живая поддержка от инженерной команды
Сейчас ребята запускают первых пользователей и собирают честную обратную связь от тех, кто реально работает с инфраструктурой.
Можно зайти, потыкать, взять VPS под задачу и написать фидбек.
👉 Serverum.ru
2 518
Контроль роста логов systemd по сервисам
Когда
/var/log начинает раздуваться, виноват почти всегда один-два шумных сервиса. Но в journald нет привычных файлов - значит нужно считать по-другому. Значит будем брать сервис, считать объем его логов и сравниваем во времени.
1️⃣ Сколько логов пишет сервис. Пример для nginx:
journalctl -u nginx --since "1 hour ago" | wc -c
Получаем объем логов за час (в байтах).
2️⃣ Быстрый топ шумных сервисов
systemctl list-units --type=service --no-legend | awk '{print $1}' |
while read -r svc; do
size=$(journalctl -u "$svc" --since "1 hour ago" 2>/dev/null | wc -c)
echo "$size $svc"
done | sort -nr | head
Результат:
12345678 docker.service
9876543 nginx.service
1234567 ssh.service
Сразу видно, кто генерирует больше всего логов.
3️⃣ Контроль роста. Сохраняем текущее состояние:
journalctl -u nginx --since "1 hour ago" | wc -c > /tmp/nginx.size
Через время:
old=$(cat /tmp/nginx.size)
new=$(journalctl -u nginx --since "1 hour ago" | wc -c)
echo "Рост: $((new - old)) байт"
4️⃣ Скрипт
#!/usr/bin/env bash
SINCE="10 min ago"
THRESHOLD=1000000 # 1 MB
systemctl list-units --type=service --no-legend | awk '{print $1}' |
while read -r svc; do
size=$(journalctl -u "$svc" --since "$SINCE" 2>/dev/null | wc -c)
if (( size > THRESHOLD )); then
echo "ALERT: $svc → $size bytes за $SINCE"
fi
done
BashTex 📱 #systemd #logs2 518
Как правильно обрабатывать stdin в скриптах
Хороший CLI-скрипт должен уметь принимать данные: из файла, из pipe (|) и из stdin. И делать это предсказуемо.
▪️ Проверка: есть ли stdin
if [ -t 0 ]; then
echo "stdin пуст (ввод с терминала)"
else
echo "данные пришли через pipe"
fi
-t 0 - stdin подключен к терминалу
иначе - есть входной поток
▪️ Базовый шаблон
#!/usr/bin/env bash
if [ -t 0 ]; then
echo "Usage: command < file | pipe"
exit 1
fi
while IFS= read -r line; do
echo ">> $line"
done
Теперь скрипт работает так:
cat file.txt | script.sh
▪️ Универсальный вариант (stdin + файл)
input="${1:-/dev/stdin}"
while IFS= read -r line; do
echo "$line"
done < "$input"
Теперь можно:
script.sh file.txt
cat file.txt | script.sh
🤩 Частая ошибка
for line in $(cat file.txt); do
echo "$line"
done
Она ломает: пробелы, табы и пустые строки
🤩 Правильно
while IFS= read -r line; do
echo "$line"
done < file.txt
BashTex 📱 #scripts #stdin
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
