2016-12-16 3 views
1

Приложение My Go выводит некоторые количества текстовых данных, и мне нужно передать его какой-либо внешней команде (например, less). Я не нашел способа передать эти данные в процесс syscall.Exec.Труба к процессу exec'ed

В качестве обходного пути я пишу, что текстовые данные во временный файл, а затем использовать этот файл в качестве аргумента less:

package main 

import (
    "io/ioutil" 
    "log" 
    "os" 
    "os/exec" 
    "syscall" 
) 

func main() { 
    content := []byte("temporary file's content") 
    tmpfile, err := ioutil.TempFile("", "example") 
    if err != nil { 
     log.Fatal(err) 
    } 

    defer os.Remove(tmpfile.Name()) // Never going to happen! 

    if _, err := tmpfile.Write(content); err != nil { 
     log.Fatal(err) 
    } 
    if err := tmpfile.Close(); err != nil { 
     log.Fatal(err) 
    } 

    binary, err := exec.LookPath("less") 
    if err != nil { 
     log.Fatal(err) 
    } 

    args := []string{"less", tmpfile.Name()} 

    if err := syscall.Exec(binary, args, os.Environ()); err != nil { 
     log.Fatal(err) 
    } 
} 

Он работает, но оставляет временный файл в файловой системе, так как syscall.Exec заменяет текущий процесс Go с другим (less) один и отложенный os.Remove не запускается. Такое поведение нежелательно.

Есть ли способ передать некоторые данные во внешний процесс, не оставляя никаких артефактов?

ответ

2

Вы должны использовать os/exec для создания exec.Cmd для выполнения, тогда вы можете поставить любой io.Reader, который вы хотите как команду stdin для команды.

Из примера в документации:

cmd := exec.Command("tr", "a-z", "A-Z") 
cmd.Stdin = strings.NewReader("some input") 
var out bytes.Buffer 
cmd.Stdout = &out 
err := cmd.Run() 
if err != nil { 
    log.Fatal(err) 
} 
fmt.Printf("in all caps: %q\n", out.String()) 

Если вы хотите писать непосредственно стандартного ввода данной команды, то вы звоните cmd.StdInPipe получить io.WriteCloser вы можете написать.

Если вам действительно нужно сделать exec процесс вместо текущего, вы можете просто удалить файл перед выполнением exec'ing и предоставить этот дескриптор файла как stdin для программы.

content := []byte("temporary file's content") 
tmpfile, err := ioutil.TempFile("", "example") 
if err != nil { 
    log.Fatal(err) 
} 
os.Remove(tmpfile.Name()) 

if _, err := tmpfile.Write(content); err != nil { 
    log.Fatal(err) 
} 

tmpfile.Seek(0, 0) 

err = syscall.Dup2(int(tmpfile.Fd()), syscall.Stdin) 
if err != nil { 
    log.Fatal(err) 
} 

binary, err := exec.LookPath("less") 
if err != nil { 
    log.Fatal(err) 
} 

args := []string{"less"} 

if err := syscall.Exec(binary, args, os.Environ()); err != nil { 
    log.Fatal(err) 
} 
+0

Спасибо, я знаю о 'os/exec' * et al *, но это не решит мою проблему вообще. Я хочу передать данные в пейджеровую среду ('less'), а не обрабатывать некоторые текстовые утилиты и обрабатывать выходные данные. –

+0

@PetrShevtsov: Я неправильно понял, потому что вы не можете напрямую подключиться к процессу, который вы собираетесь выполнять. Труба блокируется и данные должны быть где-то буферизованы. Это не проблема Go, потому что вы не можете этого делать в целом. Если вы находитесь в системе posix (которую я предполагаю с вызовами syscall.Exec и 'less'), вы можете просто удалить файл tmp. – JimB

+0

спасибо, это работает! Но могу ли я избавиться от временного файла и написать непосредственно на 'syscall.Stdin'? –

Смежные вопросы