Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
Post #384
1.11K
BA @bash_tex
Showing posts older than #385 · Back to latest
HISTCONTROL=ignoreboth
# обычная команда - сохраняется в истории
echo "hello"
# команда с пробелом - не будет в history
echo "secret run"
.bashrc:
export HISTCONTROL=ignoreboth
export HISTSIZE=5000
export HISTFILESIZE=10000
ss -s
Total: 1024 (kernel 2048)
TCP: 512 (estab 200, closed 10, orphaned 0, synrecv 0, timewait 55/0), ports 0
UDP: 256
RAW: 0
ss -tn state established '( dport != :22 )' \
| awk '{print $5}' \
| sed 's/:[0-9]*$//' \
| sort \
| uniq -c \
| sort -nr \
| head -n 20
120 203.0.113.42
87 198.51.100.10
45 10.0.0.5
...
ss -tn state established \
| awk '{print $4}' \
| sed 's/.*://; s/::ffff://g' \
| sort \
| uniq -c \
| sort -nr \
| head -n 20
340 443
200 80
50 22
...
ss -tnp | awk '/ESTAB/ { split($6, a, ":"); ip=a[1]; match($0, /pid=[0-9]+,/, m); pid=(m[0]=="")? "?" : substr(m[0],5,length(m[0])-5); print ip, pid }' \
| sort | uniq -c | sort -nr | head -n 30
50 203.0.113.42 2345
30 198.51.100.10 5012
...
ss -tnp | awk '/ESTAB/ { if (match($0, /pid=([0-9]+),/ , m)) { pid=m[1]; sub(/^.*pid=[0-9]+, /,""); proc=$0; split(proc,p," "); print pid, p[1] } }' \
| sort | uniq -c | sort -nr | head -n 20
# создать chain для мониторинга (однократно)
sudo iptables -N MONITOR 2>/dev/null || true
sudo iptables -I INPUT -j MONITOR
sudo iptables -I OUTPUT -j MONITOR
# добавить правило для конкретного IP (пример)
sudo iptables -I MONITOR -s 203.0.113.42 -j ACCEPT
# посмотреть счётчики
sudo iptables -L MONITOR -vn --line-numbers
ss -lnpt | awk '{print $4}' | sed 's/.*://; /^[[:space:]]*$/d' | sort | uniq -c | sort -nr
ss -un | awk '{print $5}' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -nr | head
ss -tnp | sed 's/ */ /g' | awk '{print $1, $4, $5, $6}'
export PROMPT_COMMAND='[[ $curdir != $PWD ]] && ls -a; curdir=$PWD'
PROMPT_COMMAND='[[ $lastdir != $PWD ]] && ls; lastdir=$PWD'
PROMPT_COMMAND='branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); PS1="[\u@\h \W${branch:+ ($branch)}]\\$ "'
PROMPT_COMMAND='jobs_count=$(jobs -p | wc -l); PS1="[jobs:$jobs_count] \u@\h:\w\$ "'
PROMPT_COMMAND='PS1="[\t] \u@\h:\w\$ "'
PROMPT_COMMAND='[ $? -ne 0 ] && echo -ne "\a"'
PROMPT_COMMAND='history -a; history -c; history -r'
PROMPT_COMMAND='
[[ $lastdir != $PWD ]] && ls; lastdir=$PWD;
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null);
jobs_count=$(jobs -p | wc -l);
PS1="[jobs:$jobs_count ${branch:+git:$branch}] \u@\h:\w\$ "
'
tar cz dir_to_copy \
| base64 \
| ssh user@remote "base64 -d | tar xz"
dir_to_copy упаковывается в tar.gz, кодируется base64, передается по SSH и на удаленном хосте раскодируется и распаковывается.
tar cz myfile.txt | base64
base64 -d | tar xz
tar cz project | base64 | tmux load-buffer -
tmux save-buffer - | base64 -d | tar xz
tar cz bigdir | pv | base64 | ssh host "base64 -d | tar xz"
# На сервере A:
find /etc -type f -print0 \
| sort -z \
| xargs -0 md5sum > /tmp/etc-hash.txt
# На сервере B:
find /etc -type f -print0 \
| sort -z \
| xargs -0 md5sum > /tmp/etc-hash.txt
diff -u serverA/etc-hash.txt serverB/etc-hash.txt
for h in server1 server2; do
ssh $h 'find /etc -type f -print0 \
| sort -z \
| xargs -0 md5sum' \
| sed "s|^|$h |"
done > all-hashes.txt
awk '{print $2,$1}' all-hashes.txt | sort | uniq -c
docker-compose или kubectl apply напрямую, но часто в реальной инфраструктуре требуются условные проверки, ветвления, откаты, повторные попытки. И тут bash оказывается неожиданно полезным инструментом.
#!/usr/bin/env bash
set -euo pipefail
echo "[*] Deploying database..."
docker-compose -f compose.db.yml up -d
sleep 10
echo "[*] Checking DB health..."
docker-compose exec db pg_isready -U postgres || {
echo "DB is not ready, aborting!"
exit 1
}
echo "[*] Deploying app..."
docker-compose -f compose.app.yml up -d
#!/usr/bin/env bash
set -euo pipefail
NAMESPACE="staging"
echo "[*] Applying secrets..."
kubectl apply -f secrets.yml -n $NAMESPACE
echo "[*] Deploying app..."
kubectl apply -f app.yml -n $NAMESPACE
echo "[*] Waiting for rollout..."
kubectl rollout status deployment/myapp -n $NAMESPACE --timeout=60s || {
echo "Rollout failed, rolling back..."
kubectl rollout undo deployment/myapp -n $NAMESPACE
}
source .env
echo "[*] Running migrations..."
docker-compose run --rm app ./manage.py migrate || exit 1
echo "[*] Applying manifests..."
kubectl apply -f k8s/ -n $NAMESPACE
shopt -s extglob
?(pattern) - 0 или 1 совпадение
*(pattern) - 0 или больше
+(pattern) - 1 или больше
@(pattern) - ровно одно из
!(pattern) - всё, кроме
for f in !(*.bak); do
echo "Обрабатываю $f"
done
for img in *.@(jpg|png|gif); do
echo "Найдено изображение: $img"
done
case $var in
+([0-9])) echo "Это число" ;;
?(http)://*) echo "Это URL" ;;
*.@(sh|bash)) echo "Это Bash-скрипт" ;;
*) echo "Что-то другое" ;;
esac
for f in !(*.log|*.tmp); do
echo "Чистый файл: $f"
done
if [[ $user == +([a-zA-Z0-9._-]) ]]; then
echo "Имя пользователя валидно"
else
echo "Некорректное имя"
fi
tar -czf backup.tar.gz /data каждый раз сжимает все файлы → при больших каталогах это очень долго.
find /data -type f -mtime -1 -print0 | tar --null -czf backup-$(date +%F).tar.gz --files-from=-
-mtime -1 - ищет файлы, измененные за последние 24 часа-print0 + --null - защита от пробелов в именах--files-from=- - tar берет список файлов прямо из stdin
find /data -type f -mmin -120 -print0 | tar --null -czf backup-$(date +%F_%H%M).tar.gz --files-from=-
touch /tmp/last-backup
find /data -type f -newer /tmp/last-backup -print0 | \
tar --null -czf backup-$(date +%F).tar.gz --files-from=-
touch /tmp/last-backup
rsync -av --delete /data/ /backup/ # бэкап данных на внешний диск
ssh user@host # тест сервера
docker exec -it web bash # залезть внутрь контейнера nginx
find /var/log -type f -size +100M # поиск больших логов
apt update && apt upgrade # [system-maintenance]
history | grep "\[system-maintenance\]"
# Файл для хранения закладок
J_BOOKMARKS=~/.j_bookmarks
# Функция j
j() {
# без аргументов - показать список закладок
if [[ $# -eq 0 ]]; then
cat "$J_BOOKMARKS" 2>/dev/null || echo "Закладок пока нет."
return
fi
case "$1" in
+) # добавить закладку для текущей директории
echo "$2:$(pwd)" >> "$J_BOOKMARKS"
echo "Добавлено: $2 → $(pwd)"
;;
-) # удалить закладку
grep -v "^$2:" "$J_BOOKMARKS" > "$J_BOOKMARKS.tmp" && mv "$J_BOOKMARKS.tmp" "$J_BOOKMARKS"
echo "Удалено: $2"
;;
*) # перейти по метке
local target
target=$(grep "^$1:" "$J_BOOKMARKS" | cut -d: -f2-)
if [[ -n "$target" ]]; then
cd "$target" || echo "Ошибка: нет доступа к $target"
else
echo "Нет закладки: $1"
fi
;;
esac
}
j + myproj
/home/user/projects/myproj
j myproj
/home/user/projects/myproj
j - myproj
j
touch file-{1,2,3}.md
file-1.md
file-2.md
file-3.md
mkdir backup-{2022..2025}
backup-2022 backup-2023 backup-2024 backup-2025
touch part-{a..d}.txt
part-a.txt part-b.txt part-c.txt part-d.txt
echo {dev,staging,prod}-{us,eu,asia}
dev-us staging-us prod-us dev-eu staging-eu prod-eu dev-asia staging-asia prod-asia
cp config.{yml,json} /etc/myapp/
touch user-{001..100}.log
cp -r src{,-backup}
mv report_{2022..2024}_{01..12}.csv /data/reports/
sorted/
├── 2018/
│ ├── 01/
│ └── 07/
├── 2020/
│ ├── 12/
└── 2024/
├── 02/
└── 05/
#!/bin/bash
SRC_DIR=${1:-"./media"} # откуда берем файлы
DST_DIR=${2:-"./sorted"} # куда складываем
mkdir -p "$DST_DIR"
find "$SRC_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.mp4" -o -iname "*.mov" \) | while read -r file; do
# получаем дату модификации файла (год и месяц)
year=$(date -r "$file" +%Y)
month=$(date -r "$file" +%m)
# создаем директорию и переносим
target="$DST_DIR/$year/$month"
mkdir -p "$target"
echo "→ $file → $target/"
mv "$file" "$target/"
done
chmod +x media_sorter.sh
./media_sorter.sh ./Camera ./SortedMedia
exiftool '-FileName<CreateDate' -d "%Y/%m/%Y-%m-%d_%H-%M-%S%%-c.%%e" ./media
^ошибка^правильно
sl -l #неверная команда
bash: sl: command not found
^sl^ls # исправление
ls -l
!! - повторяет последнюю команду!ls - повторяет последнюю команду, начинавшуюся с ls!$ - последний аргумент предыдущей команды!* - все аргументы предыдущей команды
tar -xf archive.tar.gz /tmp
cd !$
cd /tmp
myfunc >out.log 2>err.log. Но что, если нужно внутри скрипта гибко ловить stdout/stderr отдельных функций и даже подпрограмм, не ломая общий вывод? Тут могут помочь динамические файловые дескрипторы через exec {fd}>.
logfile="stdout.log"
myfunc() {
echo "info: started"
echo "debug: internal"
echo "error: fail" >&2
}
exec {fd}> "$logfile" # создаем FD
myfunc 1>&$fd # stdout -> в файл, stderr остаётся на экран
exec {fd}>&- # закрываем FD
errlog="stderr.log"
exec {fd}> "$errlog"
{ ls /root; echo "done"; } 2>&$fd
exec {fd}>&-
exec {dbg}> debug.log # отдельный канал
mycalc() {
echo "42" # обычный результат
echo "step1 ok" >&$dbg
echo "step2 ok" >&$dbg
}
result=$(mycalc)
echo "result = $result"
exec {dbg}>&-
mycmd() { echo "normal out"; }
{
exec {fd}> redirected.log
mycmd >&$fd
exec {fd}>&-
}
echo "done"