2016-07-07 14 views
1

Ich versuche, eine Remote-Datei über ssh Den folgenden Ansatz auf Shellgolang scp Datei mit Krypto/ssh

funktioniert
ssh hostname "tar cz /opt/local/folder" > folder.tar.gz 

jedoch der gleiche Ansatz auf golang zum Download einiger Unterschiede in der Ausgabe Artefakt Größe zu geben. Zum Beispiel die gleichen Ordner mit reiner Shell produzieren Artefakt gz-Datei 179B und das gleiche mit go Skript 178B. Ich nehme an, dass etwas von io.Reader oder Sitzung wurde früher geschlossen wurde geschlossen. Bitte bittet euch, zu helfen.

Hier ist das Beispiel meines Skript:

func executeCmd(cmd, hostname string, config *ssh.ClientConfig, path string) error { 
    conn, _ := ssh.Dial("tcp", hostname+":22", config) 
    session, err := conn.NewSession() 
    if err != nil { 
     panic("Failed to create session: " + err.Error()) 
    } 

    r, _ := session.StdoutPipe() 
    scanner := bufio.NewScanner(r) 

    go func() { 
     defer session.Close() 

     name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix()) 
     file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) 
     if err != nil { 
      panic(err) 
     } 
     defer file.Close() 
     for scanner.Scan() { 
      fmt.Println(scanner.Bytes()) 
      if err := scanner.Err(); err != nil { 
       fmt.Println(err) 
      } 

      if _, err = file.Write(scanner.Bytes()); err != nil { 
       log.Fatal(err) 

      } 
     } 
    }() 

    if err := session.Run(cmd); err != nil { 
     fmt.Println(err.Error()) 
     panic("Failed to run: " + err.Error()) 
    } 

    return nil 
} 

Dank!

+1

Ein Scanner ist für Newline getrennte Text, tun Sie es nicht für binäre Daten (es entfernt die Zeilenumbrüche) – JimB

+0

dank JimB, kann u pls raten, was für binäre Daten verwendet werden kann? – neveragny

+1

Es ist auch sicherer, etwas Einzigartigeres als Sekunden zu verwenden, um einen eindeutigen Dateinamen wie 'ioutil.TempFile' oder zufällige Zeichenfolgen zu erzeugen, da 2 schnelle Ausführungen Ihres Programms oder gleichzeitige Ausführungen kollidieren können. – JimB

Antwort

3

bufio.Scanner ist für durch Zeilentrennzeichen getrennter Text. Laut der Dokumentation entfernt der Scanner die Newline-Zeichen und entfernt alle 10 s aus Ihrer Binärdatei.

Sie benötigen keine Goroutine, um die Kopie zu erstellen, da Sie session.Start verwenden können, um den Prozess asynchron zu starten.

Sie müssen wahrscheinlich auch nicht bufio verwenden. Sie sollten io.Copy verwenden, um die Datei zu kopieren, die bereits über einen Puffer verfügt, der bereits im ssh-Client selbst gepuffert wurde. Wenn ein zusätzlicher Puffer für die Leistung benötigt wird, müssen Sie die Session-Ausgabe in eine bufio.Reader

schlüpfen. Schließlich geben Sie einen Fehlerwert zurück, also verwenden Sie ihn lieber als Panik bei normalen Fehlerbedingungen.

conn, err := ssh.Dial("tcp", hostname+":22", config) 
if err != nil { 
    return err 
} 

session, err := conn.NewSession() 
if err != nil { 
    return err 
} 
defer session.Close() 

r, err := session.StdoutPipe() 
if err != nil { 
    return err 
} 

name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix()) 
file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) 
if err != nil { 
    return err 
} 
defer file.Close() 

if err := session.Start(cmd); err != nil { 
    return err 
} 

n, err := io.Copy(file, r) 
if err != nil { 
    return err 
} 

if err := session.Wait(); err != nil { 
    return err 
} 

return nil 
+0

Danke für Ihre Antwort, es hat mir sehr geholfen. – neveragny

+0

Nur eine kurze Frage auf io.Copy. Der Grund, warum ich io.Reader benutze, ist, dass ich große Tear-Pakete mit je 15-30GB übertragen muss, da ich weiß, dass io.Copy versucht, es im Speicher zu behalten, bis es EOF findet? Ist das richtig? – neveragny

+0

@neveragny: Nein, ich bin mir nicht sicher, was dir diesen Eindruck gegeben hat. Kopieren Sie Kopien direkt von src nach dst. – JimB

-1

können Sie versuchen, so etwas wie tun:

r, _ := session.StdoutPipe() 
reader := bufio.NewReader(r) 

go func() { 
    defer session.Close() 
    // open file etc 

    // 10 is the number of bytes you'd like to copy in one write operation 
    p := make([]byte, 10) 
    for { 
     n, err := reader.Read(p) 
     if err == io.EOF { 
      break 
     } 
     if err != nil { 
      log.Fatal("err", err) 
     } 

     if _, err = file.Write(p[:n]); err != nil { 
      log.Fatal(err) 
     } 
    } 
}() 

Stellen Sie sicher, dass Ihre goroutines synchronisiert richtig so Ausgabe completeky in die Datei geschrieben wird.

+0

10 ist keine nützliche Größe für einen Puffer, es ist viel zu klein und ist nicht mit einer Potenz von 2 ausgerichtet. Sie müssen auch prüfen, ob Bytes _before_ nach EOF gelesen haben, damit Sie den Stream nicht unbeabsichtigt abschneiden. – JimB

+0

Ja, es ist eher ein Platzhalter. Was wäre aber eine gute Größe? – abhink

+1

io.Copy verwendet standardmäßig 32 KB. Verwenden Sie einfach io.Copy (oder CopyBuffer, wenn Sie eine andere Puffergröße benötigen), da es verschiedene Fehlerkombinationen richtig behandelt. – JimB