Skip to content

Bash: операторы, перенаправления, echo и sed — для автоматизации сетевика ​

Зачем: наладчик с bash экономит часы: автоматический опрос свитча, массовый ping подсети, разбор логов, генерация отчётов. Всё ниже — на реальных примерах из скриптов нашей лаборатории (они лежат в ~/NetLab/*.sh).


1. Коды выхода: язык «успех/ошибка» любой команды ​

Каждая команда в Linux завершаясь оставляет код выхода (exit code): 0 = успех, всё остальное = ошибка (конкретное число — детали). Увидеть код последней команды:

bash
ping -c1 -W1 192.168.100.1 >/dev/null && echo "было ок"
echo $?        # напечатает 0 (успех) или 1 (не дотянулись)

Всё в скриптинге строится на этом: условия, &&/||, if, set -e.

2. Операторы &&, ||, ; ​

ОператорЧто делаетКогда использовать
;выполнить следующую команду всегда, независимо от результатапорядок действий без связи
&&выполнить следующую только если предыдущая успешна (код 0)цепочки «и так далее, если всё ок»
``
bash
# пример из нашей жизни: скопировать конфиг, и ТОЛЬКО потом перезапустить
cp chr.rsc /backup/ && ssh chr '/import file-name=chr.rsc'

# если пинг не прошёл — вывести тревогу
ping -c1 -W2 192.168.100.1 >/dev/null || echo "АЛАРМ: шлюз не отвечает"

# комбинация: попробовать основной DNS, при фейле — запасной
nslookup ya.ru 192.168.100.1 || nslookup ya.ru 1.1.1.1

# целая цепочка: обновить → поставить → перезапустить, стоп при любом фейле
apk update && apk add tcpdump && rc-service sshd restart

Мнемоника: && — «и тогда», || — «иначе». Читается слева направо как предложение.

3. echo и printf: печать и её подводные камни ​

bash
echo "Привет, сеть"           # просто вывод строки
echo -n "без перевода строки"  # -n убирает \n в конце
echo "переменная: $HOSTNAME"  # двойные кавычки — переменные подставляются
echo 'буквально: $HOSTNAME'   # одинарные — НИЧЕГО не подставляется
echo "результат: $(ip r | head -1)"   # $(...) — подстановка вывода команды
printf "%-12s %s\n" "шлюз:" "192.168.100.1"   # форматированный вывод (таблички!)

Правило кавычек (самое частое падение новичка):

  • двойные "..." — подставляются переменные и $(...), но пробелы внутри сохраняются как одно слово;
  • одинарные '...' — вообще ничего не трогается, текст как есть;
  • без кавычек $var с пробелами развалится на куски — всегда пиши "$var".

4. Перенаправления: куда уходит вывод ​

Три «ручейка» у каждой команды: stdout (обычный вывод, fd 1), stderr (ошибки, fd 2), stdin (ввод, fd 0).

bash
команда > file       # stdout → в файл (ПЕРЕЗАПИСАТЬ файл!)
команда >> file      # stdout → ДОПИСАТЬ в конец файла
команда 2> err.log   # stderr → в файл (ошибки отдельно)
команда > out.log 2> err.log    # развести потоки по файлам
команда > all.log 2>&1          # ВСЁ в один файл (&1 = «туда же, куда stdout»)
команда 2>/dev/null  # /dev/null — «чёрная дыра», молча выбросить ошибки
команда < file       # подать файл на вход команды

Типовая иллюстрация из нашей лабы (кейс №2):

bash
ping -c1 -W2 192.168.100.1 >/dev/null || echo "шлюз мёртв"
#               ^^^^^^^^^ вывод пинга НЕ показываем юзеру, интересен только факт

«Почему cat > file сразу пишет в файл» ​

cat без аргументов читает stdin. Когда ты пишешь cat > notes.txt, shell:

  1. открывает (создаёт/обнуляет) notes.txt на запись;
  2. подключает его к stdout cat'а;
  3. cat ждёт ввода с клавиатуры, и каждая строка сразу падает в файл (Ctrl+D — конец ввода).

> выполняется shell'ом до запуска команды — поэтому файл создаётся даже если команда упадёт, и именно поэтому cat > file опасно: опечатка в имени стирает не тот файл. Проверяй, потом жми Enter.

Тот же трюк с heredoc (многострочный текст прямо в скрипте):

bash
cat > /etc/network/interfaces <<'EOF'
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
	address 192.168.100.11/24
	gateway 192.168.100.1
EOF
# <<'EOF' с кавычками — внутри НЕ подставляются переменные (важно для конфигов!)
# <<EOF   без кавычек — подстановка разрешена

Этим приёмом я собирал /etc/network/interfaces на Alpine одной командой по SSH.

5. Пайпы |: конвейер обработки ​

bash
ip neigh | grep REACHABLE | wc -l        # сколько живых соседей
cat /var/log/messages | grep -i dhcp     # фильтр по логу
dmesg | tail -20                          # последние 20 строк

Каждая команда получает stdout предыдущей на свой stdin. Пайп не создаёт промежуточных файлов — данные текут в памяти. Дальше — tee, который «раздваивает» поток (видишь на экране И пишешь в файл):

bash
ping ya.ru | tee ping-report.txt      # показывает пинг и пишет отчёт
echo "192.168.100.1 gw" | tee -a hosts   # -a = дописать

6. Переменные, условия, циклы — минимальный набор ​

bash
GW="192.168.100.1"                    # переменная, без пробелов вокруг =
ping -c1 -W2 "$GW" >/dev/null
if [ $? -eq 0 ]; then                 # $? — код выхода предыдущей команды
  echo "$GW жив"
else
  echo "$GW не отвечает"
fi

# короткая форма без if:
ping -c1 -W2 "$GW" >/dev/null && echo "жив" || echo "мёртв"

# цикл по списку (массовая проверка клиентов подсети):
for ip in 11 12 13 14 15; do
  ping -c1 -W1 "192.168.100.$ip" >/dev/null && echo ".100.$ip OK" || echo ".100.$ip DOWN"
done

# цикл по файлу (например, список свитчей):
while read -r host; do
  echo "=== $host ==="
  ping -c1 -W2 "$host" >/dev/null || echo "$host недоступен"
done < switches.txt

[ ... ] — это команда test (проверки: -f файл существует, -z строка пуста, -eq числа равны, = строки равны). [ "$a" = "$b" ] — пробелы вокруг скобок и = обязательны!

set -e в начале скрипта = «падать при любой ошибке» — двойственная штука: спасает от полуработающих скриптов, но требует явных || true там, где фейл допустим (пример из нашего create-chr.sh: virsh destroy chr 2>/dev/null || true).

7. sed: найти и заменить (регулярные выражения) ​

sed читает текст построчно и применяет команды. Королевская команда — замена:

bash
sed 's/старое/новое/' file.txt     # заменить ПЕРВОЕ вхождение в каждой строке (вывод в stdout)
sed 's/старое/новое/g' file.txt    # g — ВСЕ вхождения в строке
sed -i 's/старое/новое/g' file.txt # -i — править файл НА МЕСТЕ (in-place, бэкап потом не вспомнишь — сначала копия!)

Синтаксис s/.../.../… — «s» = substitute. Между slash'ами: шаблон / замена / флаги.

Мини-словарь регулярных выражений (хватает на 90% задач) ​

ШаблонЗначит
.любой один символ
*предыдущий символ/группа: 0 или много раз (192.* — «192.» и дальше что угодно)
^начало строки; $ — конец строки
[0-9]любая цифра; [a-z] — любая маленькая буква
\+один или больше (в sed пиши \+, в grep -E просто +)
\( \)скобки-«карманы»: пойманное потом достаётся как \1, \2…

Примеры из реальной жизни наладчика ​

bash
# поменять IP шлюза в конфиге Alpine (делал так в лабе):
sed -i 's/100\.11/100\.12/' /etc/network/interfaces
#          ^^ точки экранируем \. — иначе «.» = «любой символ»!

# выдернуть IP из вывода (карманы):  ip -br a → «eth0  UP  192.168.100.11/24 …»
ip -br a show eth0 | sed -n 's/.* \([0-9.]*\)\/24.*/\1/p'
#   -n + /p — печатать только строки, где сработала замена
#   \([0-9.]*\) — карман «последовательность цифр и точек», \/24 — до маски /24

# закомментировать строку в конфиге:
sed -i 's|^gateway 192.168.100.1|#&|' /etc/network/interfaces
#   ^ — начало строки; & — «всё, что нашлось» (полная замена на себя-же с #)

# заменить только на 3-й строке:
sed -i '3s/dhcp/static/' /etc/network/interfaces

Два золотых правила sed:

  1. Сначала без -i — смотри результат на stdout; убедился → добавляй -i. Опечатка в -i-команде над важным конфигом = «вчера всё работало».
  2. Экранируй точки, когда ищешь IP: 192.168.1.1 в regex — это «192<любой символ>168<любой>1<любой>1», а 192\.168\.1\.1 — строго IP.

sed vs grep vs awk (когда что) ​

  • grep — отфильтровать строки по шаблону (grep -v — наоборот, НЕ подходящие);
  • sed — изменить строки (замены, удаление строк 5d, вставка);
  • awk — посчитать/разобрать по колонкам (awk '{print $1}' — первая колонка, awk '$3>100' — где третья колонка больше 100). Частая связка: awk '{print $2}' | sort | uniq -c | sort -rn — «частотная таблица чего угодно» (топ IP в логе!).

8. Собери всё вместе: боевой пример ​

Скрипт «утренний обход сети» — тот, что положат в портфолио:

bash
#!/bin/bash
# morning-check.sh — проверка доступности хостов из списка
set -euo pipefail   # -e падать при ошибке, -u кричать на пустые переменные,
                    # pipefail считать упавшим пайп, если упал ЛЮБОЙ его элемент

LIST="${1:-hosts.txt}"   # список хостов: первый аргумент или hosts.txt по умолчанию
REPORT="report-$(date +%H%M).txt"

echo "=== Обход сети $(date) ===" | tee "$REPORT"
while read -r host; do
  [ -z "$host" ] && continue                 # пропустить пустые строки
  ping -c2 -W2 "$host" >/dev/null 2>&1 \
    && echo "[ OK ] $host" | tee -a "$REPORT" \
    || echo "[FAIL] $host" | tee -a "$REPORT"
done < "$LIST"

echo "Готово: $REPORT"

Запуск: chmod +x morning-check.sh && ./morning-check.sh. Каждый приём здесь ты уже знаешь: &&/||, tee, while read, 2>&1, параметр по умолчанию ${1:-...}.

9. Шпаргалка на полях ​

bash
cmd > f      # перезаписать      cmd >> f     # дописать
cmd 2> f     # ошибки в файл     cmd 2>&1 | … # всё в пайп
cmd1 && cmd2 # cmd2 только если cmd1 ок
cmd1 || cmd2 # cmd2 только если cmd1 фейл
echo "$x"    # всегда в кавычках!
sed -i 's/a/b/g' f     # замена: сначала без -i!
grep -E 'пат(терн)?' f # -E расширенные regex

Примеры скриптов с разбором каждой строки: ~/NetLab/create-chr.sh (вводил в &&/||, set -e, переменные), ~/NetLab/type-to-vm.sh (цикл по строке, case, функции), ~/NetLab/netlab-up.sh (цикл по списку VM).