Можно ли установить стандартный ввод процесса в канал, а остальные — в устройство TTY?Linux

Ответить
Anonymous
 Можно ли установить стандартный ввод процесса в канал, а остальные — в устройство TTY?

Сообщение Anonymous »

Я пытаюсь создать процесс, чтобы я мог сделать следующее:
  • Отправить текст на его стандартный ввод и указать EOF, закрыв канал stdin.
  • Подключите стандартный вывод и стандартный поток процесса к устройству TTY, чтобы его поведение было идентично тому, когда он запускается внутри TTY.
Изначально я запустил процесс в pty с помощью creack/pty в Go, но вскоре понял, что нет способа «отправить» EOF. Я знаю, что EOF не является ни сигналом, ни символом, поэтому его нельзя отправить.
Возьмем, к примеру, поведение выделения в 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;
}
Но мне не удалось воспроизвести это поведение с помощью Go. Родитель просто зависает на функции Read()

Код: Выделить всё

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())
}
}

Пожалуйста, разъясните мне изменения, которые мне нужно внести в мою программу Go.


Подробнее здесь: https://stackoverflow.com/questions/798 ... tty-device
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Linux»