- Отправить текст на его стандартный ввод и указать EOF, закрыв канал stdin.
- Подключите стандартный вывод и стандартный поток процесса к устройству TTY, чтобы его поведение было идентично тому, когда он запускается внутри TTY.
Возьмем, к примеру, поведение выделения в grep. Я хочу, чтобы grep вёл себя так, как будто он запускается в TTY (без указания флага --color). Для этого я хочу, чтобы его stdou был подключен к устройству TTY, а не к каналу. Но в то же время я хочу отправить входные данные в grep с помощью канала, чтобы я мог закрыть канал и указать EOF для grep.

Изменить: мне удалось добиться этого на C, используя следующий фрагмент кода:
Код: Выделить всё
#define _XOPEN_SOURCE 600
#include
#include
#include
#include
#include
#include
#include
int main() {
int pipe_fd[2];
int master_fd, slave_fd;
pid_t pid;
// Create pipe for stdin
if (pipe(pipe_fd) == -1) {
perror("pipe");
exit(1);
}
// Create PTY for stdout/stderr
// This makes grep think it's running in a terminal, enabling color output
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
perror("openpty");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
// Child process
// Close unused pipe end (write end)
close(pipe_fd[1]);
// Redirect stdin to pipe read end
dup2(pipe_fd[0], STDIN_FILENO);
close(pipe_fd[0]);
// Close master PTY (parent uses this)
close(master_fd);
// Redirect stdout and stderr to slave PTY
dup2(slave_fd, STDOUT_FILENO);
dup2(slave_fd, STDERR_FILENO);
close(slave_fd);
// Execute grep with -P flag for Perl regex
execlp("grep", "grep", "--color=auto" , "-P", "hello", NULL);
// If exec fails
perror("execlp");
exit(1);
} else {
// Parent process
// Close unused pipe end (read end)
close(pipe_fd[0]);
// Close slave PTY (child uses this)
close(slave_fd);
// Write "hello world" to grep's stdin (emulating: echo "hello world" | grep -P "hello")
const char *test_data = "hello world\n";
write(pipe_fd[1], test_data, strlen(test_data));
close(pipe_fd[1]); // Close pipe to signal EOF - grep will exit cleanly
// Read grep's output from master PTY
// This will include ANSI color codes if grep detects a TTY
char buffer[1024];
ssize_t n;
printf("=== Grep output (with color codes if terminal detected) ===\n");
while ((n = read(master_fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("%s", buffer);
}
close(master_fd);
// Wait for child to finish
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
printf("=== grep exited with status %d ===\n", WEXITSTATUS(status));
}
}
return 0;
}
Код: Выделить всё
package main
/*
#include
#include
// Wrapper function to call openpty
int open_pty(int *master_fd, int *slave_fd) {
return openpty(master_fd, slave_fd, NULL, NULL, NULL);
}
*/
import "C"
import (
"fmt"
"log"
"os"
"syscall"
)
func main() {
// Create pipe for stdin
pipeFd := make([]int, 2)
if err := syscall.Pipe(pipeFd); err != nil {
fmt.Fprintf(os.Stderr, "pipe: %v\n", err)
os.Exit(1)
}
// Create PTY for stdout/stderr using openpty(3)
var masterFd, slaveFd C.int
if C.open_pty(&masterFd, &slaveFd) == -1 {
fmt.Fprintf(os.Stderr, "openpty failed\n")
os.Exit(1)
}
pid, err := syscall.ForkExec(
"/opt/homebrew/bin/ggrep",
[]string{"ggrep", "--color=auto", "-P", "hello"},
&syscall.ProcAttr{
Files: []uintptr{
uintptr(pipeFd[0]), // stdin from pipe
uintptr(slaveFd), // stdout to PTY
uintptr(slaveFd), // stderr to PTY
},
Sys: &syscall.SysProcAttr{
Setsid: true,
},
},
)
if err != nil {
fmt.Fprintf(os.Stderr, "ForkExec: %v\n", err)
os.Exit(1)
}
// Close unused descriptors
syscall.Close(pipeFd[0]) // read end (child uses this)
syscall.Close(int(slaveFd)) // slave PTY (child uses this)
// Write test data to grep's stdin
testData := []byte("hello world\n\n")
syscall.Write(pipeFd[1], testData)
if err := syscall.Close(pipeFd[1]); err != nil {
log.Fatal(err.Error())
} // Close to signal EOF
// Read grep's output from master PTY
fmt.Println("=== Grep output (with color codes if terminal detected) ===")
buffer := make([]byte, 1024)
for {
fmt.Println("Read...")
n, err := syscall.Read(int(masterFd), buffer)
if err != nil || n == 0 {
break
}
fmt.Print(string(buffer[:n]))
}
syscall.Close(int(masterFd))
// Wait for child to finish
var status syscall.WaitStatus
_, err = syscall.Wait4(pid, &status, 0, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "wait4: %v\n", err)
os.Exit(1)
}
if status.Exited() {
fmt.Printf("=== grep exited with status %d ===\n", status.ExitStatus())
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... tty-device