Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Системное Администрирование
  4. Bash | Shell
  5. Bash Пайпы: Полный Гайд и Шпаргалка

Bash Пайпы: Полный Гайд и Шпаргалка

Запланировано Прикреплена Закрыта Перенесена Bash | Shell
1 Сообщения 1 Постеры 60 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • MugiwaraM Не в сети
    MugiwaraM Не в сети
    Mugiwara
    написал отредактировано Aladdin
    #1

    Представьте, что вам нужно найти количество файлов в директории. Без пайпов пришлось бы делать так:

    # Сначала список файлов сохраняем в файл
    ls -l > /tmp/files.txt
    
    # Потом считаем строки
    wc -l /tmp/files.txt
    
    # Потом удаляем временный файл
    rm /tmp/files.txt
    

    Три команды, временный файл, неудобно.

    Решение с пайпами

    С пайпом это становится одной строкой:

    ls -l | wc -l
    

    Почему пайпы полезны:

    1. Эффективность — не нужны временные файлы на диске
    2. Скорость — данные передаются через оперативную память (быстрее, чем I/O на диск)
    3. Читаемость — понимаете логику цепочки команд с первого взгляда
    4. Масштабируемость — можете соединять сколько угодно команд в цепочку
    5. Unix философия — “Делай одно, делай хорошо” — пайпы объединяют маленькие команды в мощные решения

    2. Основные концепции: потоки данных

    В Linux каждый процесс имеет три стандартных потока:

    Поток Номер FD Описание По умолчанию
    stdin 0 Стандартный ввод (входит в процесс) Клавиатура
    stdout 1 Стандартный вывод (выходит из процесса) Экран
    stderr 2 Стандартная ошибка (сообщения об ошибках) Экран

    Визуально:

    ┌─────────────────┐
    │     Процесс     │
    │                 │
    │  ┌──────────┐   │
    │  │ команда  │   │
    │  └──────────┘   │
    │    ↑  ↓  ↓      │
    │    │  │  │      │
    │    0  1  2      │ (file descriptors)
    │    │  │  │      │
    └────┼──┼──┼──────┘
         │  │  └─ stderr → экран
         │  └──── stdout → экран
         └─────── stdin ← клавиатура
    

    3. Как работают пайпы?

    Механизм пайпа

    Пайп (|) соединяет stdout одной команды со stdin другой:

    команда1 | команда2
        ↓
    stdout(команда1) → stdin(команда2)
    

    На системном уровне:

    1. Shell видит символ | и создаёт канал (pipe) в памяти
    2. Stdout первого процесса подключается к этому каналу
    3. Stdin второго процесса подключается к этому каналу
    4. Обе команды запускаются одновременно (параллельно!)
    5. Буферизация позволяет команде 2 работать, пока команда 1 пишет

    Важно: Команды выполняются параллельно, но синхронизированы потоком данных.

    Пример в реальном времени

    # Медленный процесс производит данные
    (while true; do date; sleep 1; done) | \
    # Быстрый процесс их обрабатывает
    (while read line; do echo ">>> $line"; done)
    

    Обе команды работают одновременно!


    4. Редирекция потоков

    Базовая редирекция stdout

    # Перенаправить вывод в файл (перезаписать)
    command > file.txt
    
    # Добавить в конец файла
    command >> file.txt
    
    # Отправить в /dev/null (удалить вывод)
    command > /dev/null
    

    Редирекция stderr

    # Отправить ошибки в файл
    command 2> errors.txt
    
    # Отправить ошибки в файл (классическая форма)
    command 2> /tmp/errors.log
    
    # Объединить stderr со stdout
    command 2>&1
    
    # Отправить оба потока в файл (современный синтаксис)
    command &> output.txt
    
    # Скрыть ошибки
    command 2> /dev/null
    

    Комбинированная редирекция

    # stdout → файл, stderr → экран
    command > output.txt 2>&1
    
    # stdout → одна команда, stderr → другая команда
    command 1> >(process_stdout) 2> >(process_stderr)
    
    # Отправить оба потока в разные файлы
    command > stdout.txt 2> stderr.txt
    
    # stdout в файл, stderr к stdout, затем в пайп
    command 2>&1 | tee output.txt
    

    Важно про порядок! Обработка идёт слева направо:

    # ПРАВИЛЬНО: сначала объединяем потоки, потом пайпим
    command 2>&1 | grep error
    
    # НЕПРАВИЛЬНО: пайпим только stdout
    command | grep error 2>&1  # ошибки не попадут в пайп!
    

    Редирекция stdin

    # Передать файл как стандартный ввод
    command < input.txt
    
    # Передать строку как стандартный ввод
    cat <<EOF | command
    множественные
    строки
    текста
    EOF
    

    5. Практические примеры

    Пример 1: Поиск и подсчёт файлов

    # Найти все .log файлы, созданные за последние 7 дней, и посчитать их
    find . -name "*.log" -mtime -7 | wc -l
    
    # Поток данных:
    # find → (список файлов в stdin) → wc
    

    Пример 2: Фильтрация логов

    # Найти ошибки в логе, исключить DEBUG, показать последние 10
    cat app.log | \
      grep "ERROR" | \
      grep -v "DEBUG" | \
      tail -10
    
    # Или более компактно:
    grep "ERROR" app.log | grep -v "DEBUG" | tail -10
    

    Пример 3: Обработка данных с преобразованием

    # Получить список портов, которые слушают, отсортировать и посчитать уникальные
    netstat -tlnp | \
      awk '{print $4}' | \
      cut -d: -f2 | \
      sort | \
      uniq -c | \
      sort -rn
    

    Разбор:

    • netstat -tlnp — показать все слушающие TCP порты
    • awk '{print $4}' — извлечь 4-е поле (адрес:порт)
    • cut -d: -f2 — вырезать только номер порта
    • sort — отсортировать
    • uniq -c — посчитать одинаковые
    • sort -rn — отсортировать по убыванию

    Пример 4: Обработка JSON

    # Получить все email'ы из JSON API и сохранить в файл
    curl -s https://api.example.com/users | \
      jq '.users[].email' | \
      tr -d '"' | \
      sort | \
      uniq > emails.txt
    

    Пример 5: Массовая переименование файлов

    # Переименовать все .jpg в .png расширение
    ls *.jpg | \
      awk '{print "mv " $1 " " substr($1, 1, length($1)-3) "png"}' | \
      bash
    
    # Или более безопасно (echo для проверки):
    ls *.jpg | \
      awk '{print "mv " $1 " " substr($1, 1, length($1)-3) "png"}' | \
      head -5  # Проверить на первых 5
    

    Пример 6: Совмещение нескольких фильтров

    # Найти топ-10 самых тяжёлых файлов
    find . -type f -exec ls -lh {} \; | \
      awk '{print $5, $NF}' | \
      sort -h -r | \
      head -10
    
    # Поток:
    # find → ls → awk (фильтруем) → sort → head
    

    Пример 7: Работа с переменными окружения

    # Получить список всех установленных пакетов, содержащих "mysql"
    apt list --installed 2>/dev/null | \
      grep "mysql" | \
      awk -F'/' '{print $1}' | \
      tee installed-mysql.txt
    
    # tee — выводит в консоль И в файл одновременно
    

    Пример 8: Пайпы с функциями в bash

    # Определить функцию для обработки
    process_line() {
      echo "Обработан: $1"
    }
    
    # Использовать с пайпом
    echo -e "line1\nline2\nline3" | \
      while read line; do
        process_line "$line"
      done
    

    6. Пайпы vs xargs

    Разница: stdout vs аргументы

    Пайп (|) передаёт данные в stdin:

    echo -e "file1.txt\nfile2.txt" | cat  # cat получает текст в stdin
    # Вывод:
    # file1.txt
    # file2.txt
    

    xargs передаёт данные как аргументы команде:

    echo -e "file1.txt\nfile2.txt" | xargs cat  # cat получит аргументы
    # Аналогично: cat file1.txt file2.txt
    

    Когда использовать xargs?

    xargs нужен когда команда НЕ читает stdin:

    # ❌ НЕПРАВИЛЬНО: rm не читает stdin
    echo "file1.txt" | rm
    
    # ✅ ПРАВИЛЬНО: xargs преобразует stdin в аргументы
    echo "file1.txt" | xargs rm
    
    # Аналогично:
    echo "file1.txt" | xargs -I {} rm {}
    

    Примеры xargs

    # Удалить все временные файлы
    find . -name "*.tmp" | xargs rm
    
    # Скопировать множество файлов
    ls *.jpg | xargs -I {} cp {} /backup/
    
    # Запустить в параллель (-P 4 = 4 процесса одновременно)
    cat file_list.txt | xargs -P 4 -I {} bash -c 'process_file "$1"' _ {}
    
    # Обработать с пользовательским разделителем
    find . -name "*.txt" -print0 | xargs -0 wc -l
    # -print0 и -0: для файлов с пробелами в имени
    

    Сравнительная таблица

    Операция Пайп xargs
    Передача данных stdin аргументы
    Примечание “Команда читает с клавиатуры” “Команда берёт от shell”
    cat ✓ ✓ (нужна)
    grep ✓ ✓ (нужна)
    rm ✗ ✓
    cp ✗ ✓
    echo ✓ ✓
    Параллелизм ✗ ✓ (-P флаг)

    7. Питфоллы и решения

    Проблема 1: Буферизация (данные не появляются)

    Проблема: При пайпировании медленного процесса вывод задерживается.

    # Команда запущена, но вывода не видно 10 секунд
    slow_command | grep pattern
    

    Причина: Многие программы используют full buffering при пайпировании (вместо line buffering при выводе на экран).

    Решение 1: stdbuf

    slow_command | stdbuf -oL grep pattern
    # -oL = line buffering для stdout
    

    Решение 2: unbuffer

    slow_command | unbuffer grep pattern
    

    Решение 3: script/expect

    slow_command 2>&1 | tee output.txt | grep pattern
    # tee заставляет команду работать в line buffering режиме
    

    Проблема 2: Обработка ошибок в пайпах

    Проблема: При ошибке в середине пайпа не видно, где именно.

    cmd1 | cmd2 | cmd3
    # Если cmd2 упал, cmd3 может не выполниться корректно
    

    Решение:

    # Проверить код возврата каждой команды
    cmd1 | cmd2 | cmd3
    
    # BASH покажет код возврата последней команды:
    echo $?  # код возврата cmd3, НЕ cmd1 и cmd2!
    
    # Правильная проверка:
    set -o pipefail  # Если хоть одна упала, весь пайп упал
    cmd1 | cmd2 | cmd3
    echo $?  # Покажет ошибку если хоть где-то была ошибка
    
    # Или в отдельной команде:
    if cmd1 | cmd2 | cmd3; then
      echo "Успешно"
    else
      echo "Ошибка в пайпе"
    fi
    

    Проблема 3: Потери данных с пробелами/спецсимволами

    Проблема:

    # Файл с пробелом в имени
    echo "my file.txt" | xargs rm
    # ❌ Попытается удалить "my" и "file.txt" отдельно
    

    Решение:

    echo "my file.txt" | xargs -I {} rm "{}"
    # Или с print0:
    find . -name "*.txt" -print0 | xargs -0 rm
    

    Проблема 4: Слишком много аргументов

    Проблема:

    ls *.txt | xargs grep "pattern"
    # При 10000+ файлов: Argument list too long
    

    Решение:

    # Ограничить количество аргументов за раз
    ls *.txt | xargs -n 100 grep "pattern"
    
    # Или более правильно:
    find . -name "*.txt" -print0 | xargs -0 -n 100 grep "pattern"
    

    Проблема 5: Потеря переменных окружения

    Проблема:

    MY_VAR="hello" && echo "test" | while read line; do echo $MY_VAR; done
    # ❌ MY_VAR пуста! while запущен в подоболочке
    

    Решение:

    MY_VAR="hello"
    echo "test" | (
      export MY_VAR
      while read line; do echo $MY_VAR; done
    )
    # ✓ Работает
    

    Проблема 6: Данные из пайпа недоступны после цикла

    Проблема:

    echo -e "1\n2\n3" | while read num; do ((sum += num)); done
    echo "Sum: $sum"  # ❌ sum пуст!
    

    Причина: while запущен в подоболочке, переменная не передаётся.

    Решение:

    sum=0
    while read num; do ((sum += num)); done < <(echo -e "1\n2\n3")
    echo "Sum: $sum"  # ✓ 6
    

    8. Шпаргалка

    Базовый синтаксис

    # Пайп
    cmd1 | cmd2
    
    # Цепочка пайпов
    cmd1 | cmd2 | cmd3 | cmd4
    
    # Редирекция stdout
    cmd > file
    cmd >> file
    cmd > /dev/null
    
    # Редирекция stderr
    cmd 2> file
    cmd 2>> file
    cmd 2> /dev/null
    
    # Объединить потоки
    cmd 2>&1
    cmd &> file
    
    # Отдельные потоки
    cmd 1> out.txt 2> err.txt
    
    # stdin из файла
    cmd < file
    
    # stdin из строки
    cmd <<< "string"
    
    # stdin из блока
    cmd <<EOF
    line1
    line2
    EOF
    

    Часто используемые инструменты

    # grep — фильтровать текст
    cmd | grep "pattern"
    cmd | grep -v "exclude"  # инверсия
    cmd | grep -i "case-insensitive"
    
    # awk — парсинг и обработка
    cmd | awk '{print $2}'      # 2-е поле
    cmd | awk -F: '{print $1}'  # с разделителем :
    cmd | awk 'NR>2'            # кроме первых 2 строк
    
    # sed — замена текста
    cmd | sed 's/old/new/'       # заменить первое
    cmd | sed 's/old/new/g'      # заменить все
    
    # sort, uniq
    cmd | sort                   # отсортировать
    cmd | sort -u               # уникальные
    cmd | sort | uniq -c        # подсчитать
    
    # cut — вырезать колонки
    cmd | cut -d: -f1           # разделитель :, 1-е поле
    
    # tr — трансформация символов
    cmd | tr 'a-z' 'A-Z'        # в верхний регистр
    cmd | tr -d '"'             # удалить кавычки
    
    # head, tail
    cmd | head -10              # первые 10 строк
    cmd | tail -5               # последние 5 строк
    
    # wc — подсчёт
    cmd | wc -l                 # строк
    cmd | wc -w                 # слов
    cmd | wc -c                 # символов
    
    # tee — дублировать вывод
    cmd | tee file.txt          # в консоль И в файл
    
    # xargs — передать как аргументы
    cmd | xargs rm              # rm для каждого результата
    cmd | xargs -n 5            # по 5 элементов за раз
    cmd | xargs -P 4            # параллельно в 4 процесса
    

    Bash скелет для надёжного скрипта

    #!/bin/bash
    set -euo pipefail  # выход при ошибке, undefined vars, pipe ошибки
    
    # Функция с пайпом
    process_data() {
      local input_file="$1"
      
      cat "$input_file" | \
        grep "pattern" | \
        awk '{print $1}' | \
        sort | \
        uniq -c | \
        sort -rn
    }
    
    # Использование
    if output=$(process_data "data.txt"); then
      echo "$output" | head -10
    else
      echo "Ошибка обработки" >&2
      exit 1
    fi
    

    Отладка пайпов

    # Показать все команды перед выполнением
    set -x
    cmd1 | cmd2 | cmd3
    set +x
    
    # Проверить каждый этап
    cmd1 > /tmp/step1.txt
    cat /tmp/step1.txt | cmd2 > /tmp/step2.txt
    cat /tmp/step2.txt | cmd3
    
    # Или с tee:
    cmd1 | tee /tmp/step1.txt | cmd2 | tee /tmp/step2.txt | cmd3
    
    # Подробная информация
    cmd 2>&1 | tee debug.log
    less debug.log
    

    Примеры для реальных задач

    Анализ лог-файлов

    # Топ ошибок за день
    grep "ERROR" /var/log/app.log | \
      awk '{print $NF}' | \
      sort | uniq -c | \
      sort -rn | head -10
    
    # Статистика по IP адресам
    cat /var/log/nginx/access.log | \
      awk '{print $1}' | \
      sort | uniq -c | \
      sort -rn | head -20
    

    Управление файлами

    # Найти и удалить все .tmp файлы старше 7 дней
    find /tmp -name "*.tmp" -mtime +7 | xargs rm -f
    
    # Архивировать логи старше 30 дней
    find /var/log -name "*.log" -mtime +30 | \
      xargs -I {} gzip {}
    
    # Скопировать структуру директорий
    find source -type d | \
      sed 's/^source/dest/' | \
      xargs mkdir -p
    

    Обработка данных

    # CSV в JSON (простой пример)
    { IFS=, read -r header; \
      while IFS=, read -r col1 col2 col3; do \
        echo "{\"col1\":\"$col1\",\"col2\":\"$col2\",\"col3\":\"$col3\"}"
      done; \
    } < data.csv
    
    # Парсинг конфиг файлов
    grep -v "^#" config.conf | \
      grep -v "^$" | \
      awk -F= '{gsub(/ /, "", $1); print $1 "=" $2}'
    

    Итоги

    Пайпы — это основа Unix философии:

    1. Простота — каждая команда делает одно
    2. Мощь — комбинирование создаёт сложные операции
    3. Эффективность — прямая передача данных через память
    4. Читаемость — логика цепочки видна в одной строке

    Помните:

    • Пайп передаёт stdout → stdin
    • xargs передаёт данные как аргументы
    • set -o pipefail для надёжности скриптов
    • Проверяйте буферизацию при медленных процессах
    • Экранируйте спецсимволы и пробелы в файлах
    1 ответ Последний ответ
    1

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com
    • Наш чат
    • Наш ТГ канал

    © 2024 - 2025 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы