Ответ
Что такое pipe
?
Pipe
(канал или "пайп"), обозначаемый символом |
, — это один из фундаментальных механизмов в Unix-подобных системах. Он позволяет направить стандартный вывод (stdout
) одной команды на стандартный ввод (stdin
) другой. Это дает возможность строить сложные цепочки команд, где каждая выполняет одну простую задачу.
Классический пример:
# Посчитать количество уникальных строк с "error" в лог-файле
cat app.log | grep "error" | sort | uniq -c
Здесь:
cat app.log
: Читает файл и выводит его содержимое вstdout
.grep "error"
: Получает данные отcat
, фильтрует строки, содержащие "error", и передает их дальше.sort
: Сортирует полученные строки.uniq -c
: Подсчитывает количество вхождений каждой уникальной строки.
Использование pipe
в Go
Философия pipe
глубоко интегрирована в Go и используется в двух основных сценариях:
1. Взаимодействие с внешними командами (os/exec
)
Вы можете программно воссоздать цепочку команд, связав их ввод и вывод.
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
)
func main() {
// Эмуляция команды: ps aux | grep "go"
cmd1 := exec.Command("ps", "aux")
cmd2 := exec.Command("grep", "go")
// Связываем вывод первой команды со вводом второй
var err error
cmd2.Stdin, err = cmd1.StdoutPipe()
if err != nil {
log.Fatal(err)
}
// Записываем вывод второй команды в буфер
var output bytes.Buffer
cmd2.Stdout = &output
// Запускаем команды
if err := cmd1.Start(); err != nil {
log.Fatal(err)
}
if err := cmd2.Start(); err != nil {
log.Fatal(err)
}
// Ждем их завершения
if err := cmd1.Wait(); err != nil {
log.Fatal(err)
}
if err := cmd2.Wait(); err != nil {
log.Fatal(err)
}
fmt.Println(output.String())
}
2. Потоковая обработка данных между горутинами (io.Pipe
)
io.Pipe()
создает в памяти канал (in-memory pipe
), который связывает io.Reader
и io.Writer
. Это мощный инструмент для потоковой передачи данных между горутинами без необходимости использовать временные файлы или большие буферы.
package main
import (
"fmt"
"io"
"os"
"time"
)
func main() {
// Создаем pipe: данные, записанные в pr, можно будет прочитать из pw.
pr, pw := io.Pipe()
// Горутина-писатель: генерирует данные и пишет в pipe.
go func() {
defer pw.Close()
for i := 0; i < 5; i++ {
fmt.Fprintf(pw, "Data packet %dn", i)
time.Sleep(time.Second)
}
}()
// Горутина-читатель (в данном случае main): читает из pipe и выводит на экран.
// Копирование блокируется, пока в pipe не появятся данные.
if _, err := io.Copy(os.Stdout, pr); err != nil {
fmt.Println(err)
}
}
Таким образом, Go использует концепцию pipe
как для работы с ОС, так и для построения эффективных и конкурентных внутренних конвейеров обработки данных.