2017-08-28 1 views
2

Je dois traiter une sortie longue d'un script et trouver des données. Ces données seront très probablement situées au tout début de la sortie. Une fois les données trouvées, je n'ai plus besoin de traiter la sortie et je peux quitter.Comment forcer golang à fermer le tuyau ouvert

Le problème que je ne peux pas arrêter de traiter la sortie parce que exec.Cmd n'a aucune fonction pour fermer la commande ouverte.

Voici un code simplifié (gestion des erreurs a été ommited):

func processOutput(r *bufio.Reader)(){ 
    for { 
     line, _, err := r.ReadLine() 
     if some_condition_meet { 
     break 
     } 
     ...... 
    } 
    return 
} 

func execExternalCommand(){ 
    cmdToExecute := "......" 
    cmd := exec.Command("bash", "-c", cmdToExecute) 
    output, _ := cmd.StdoutPipe() 

    cmd.Start() 
    r := bufio.NewReader(output) 
    go processOutput(r) 
    cmd.Wait() 
    return 
} 

Que dois-je faire à la fin à processOutput fonction pour arrêter cmd? Peut-être qu'il y a une autre façon de le résoudre.

Merci

+0

Que voulez-vous dire qu'il n'y a aucun moyen de "fermer la commande ouverte"? Vous terminez un processus en lui envoyant un signal pour quitter, ce que vous pouvez faire avec les méthodes 'Kill' ​​ou' Signal'. – JimB

+0

Si la commande lit stdin et quitte sur EOF, fermez stdin à la commande. Sinon, envoyez un signal comme mentionné dans les autres commentaires. –

+0

Juste pour ajouter un peu plus de détails, si vous fermez stdin, les écritures stdout du sous-processus lui feront recevoir un signal SIGPIPE (par défaut, à moins que ce comportement ne soit ignoré). Donc, vous envoyez un signal d'une manière ou d'une autre. –

Répondre

3

En l'état actuel, vous ne pouvez pas le faire à partir processOutput parce que tout reçoit est un bufio.Reader. Vous devrez lui passer le exec.Cmd pour qu'il puisse faire n'importe quoi avec le processus fourchu.

Pour arrêter le processus en fourche, vous pouvez lui envoyer un signal, par exemple: cmd.Process.Kill() ou cmd.Process.Signal(os.SIGINT). Voir la documentation sur exec.Cmd et os.Process.

+0

merci pour les conseils – ravnur

1

Vous pouvez probablement utiliser "context" par exemple:

package main 

import (
    "bufio" 
    "context" 
    "os/exec" 
) 

func processOutput(r *bufio.Reader, cancel context.WithCancel) { 
    for { 
     line, _, err := r.ReadLine() 
     if some_condition_meet { 
      break 
     } 
    } 
    cancel() 
    return 
} 

func main() { 
    ctx, cancel := context.WithCancel(context.Background()) 
    defer cancel() 
    cmdToExecute := "......" 
    cmd := exec.CommandContext(ctx, "bash", "-c", cmdToExecute) 
    output, _ := cmd.StdoutPipe() 

    cmd.Start() 
    r := bufio.NewReader(output) 
    go processOutput(r, cancel) 
    cmd.Wait() 
    return 
} 

En cas besoin de mettre fin avec un délai d'attente que cela pourrait fonctionner (exemple est tiré d'ici: https://golang.org/src/os/exec/example_test.go)

func ExampleCommandContext() { 
    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 
    defer cancel() 

    if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil { 
    // This will fail after 100 milliseconds. The 5 second sleep 
    // will be interrupted. 
    } 
    } 

A base exemple juste en utilisant le sommeil mais en le fermant après 1 seconde: https://play.golang.org/p/gIXKuf5Oga

+1

Cette réponse est en or, il devrait être la meilleure réponse. – tahoe