Go SDK¶
win-sshpass is not only a command-line tool but also a reusable Go library (package sshpass). You can embed SSH/SFTP/Shell capabilities into your own application.
Installation¶
Quick Start¶
package main
import (
"log"
sshpass "github.com/chuccp/win-sshpass"
)
func main() {
cfg := sshpass.NewConfig()
cfg.Host = "example.com"
cfg.User = "root"
cfg.Password = "secret"
// NewClient dials and returns a ready-to-use client.
client, err := sshpass.NewClient(cfg, sshpass.WithSignalHandler())
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Execute a command (output streams to os.Stdout/os.Stderr by default).
if err := client.Exec("uname -a"); err != nil {
log.Fatal(err)
}
}
Client Core Methods¶
NewClient¶
Establishes an SSH connection and returns a client:
cfg: Connection configuration (*sshpass.Config)opts: Optional functional options (...sshpass.Option)- If
cfg.Timeout > 0, an operation timer is armed that closes the connection when the deadline elapses
Exec¶
Executes a single remote command:
- Output streams are configured via
WithStdout/WithStderr(default:os.Stdout/os.Stderr) - Input stream is configured via
WithStdin(default:os.Stdin)
Shell¶
Starts an interactive shell:
- Auto-detects terminal, supports PTY and raw mode
- Supports dynamic terminal resizing
- Supports rz/sz file transfer (requires
WithFileSelectorconfiguration)
SFTP¶
Opens an SFTP sub-channel:
sftp, err := client.SFTP()
if err != nil {
log.Fatal(err)
}
defer sftp.Close()
// Upload file
err = sftp.Upload("./local.txt", "/tmp/remote.txt")
// Download file
err = sftp.Download("/tmp/remote.txt", "./local.txt")
// Access underlying *sftp.Client (advanced usage)
rawClient := sftp.SFTP()
Close¶
Closes the SSH connection:
- Idempotent — multiple calls return the same error
- Automatically stops timer and signal handler
TimedOut¶
Check if the failure was due to timeout:
Configuration (Config)¶
cfg := sshpass.NewConfig()
cfg.Host = "example.com"
cfg.User = "root"
cfg.Password = "secret"
cfg.Port = "22"
cfg.KeyPath = "~/.ssh/id_ed25519"
cfg.StrictHostKey = true
cfg.Timeout = 30 // Operation timeout (seconds), 0 = no limit
cfg.ConnectTimeout = 10 // TCP connection timeout (seconds)
cfg.Retries = 3 // Connection retry count
Load from Config File¶
Load Config or Password File¶
cfg, pass, err := sshpass.LoadConfigOrPasswordFile("file.txt", "", false)
if cfg != nil {
// It's a config file
} else {
// It's a password file; pass contains the password
}
Functional Options¶
Configure Client behavior through functional options:
I/O Streams¶
// Redirect input/output
client, err := sshpass.NewClient(cfg,
sshpass.WithStdin(myReader),
sshpass.WithStdout(myWriter),
sshpass.WithStderr(myWriter),
)
Progress Callback¶
client, err := sshpass.NewClient(cfg,
sshpass.WithProgress(func(desc string, sent, total int64) {
fmt.Printf("\r%s %d/%d bytes", desc, sent, total)
}),
)
desc: Transfer description (e.g., "Uploading file.txt")sent: Bytes transferred so fartotal: Total file size- The SDK performs no rendering — callers display progress however they like
File Selector¶
type myFileSelector struct{}
func (s myFileSelector) OpenFile() (string, error) {
// Implement file open dialog
return "/path/to/file", nil
}
func (s myFileSelector) SaveFile(defaultName string) (string, error) {
// Implement file save dialog
return "/path/to/save", nil
}
client, err := sshpass.NewClient(cfg,
sshpass.WithFileSelector(myFileSelector{}),
)
Used for rz/sz shell transfer file selection. The SDK ships no default implementation.
Signal Handler¶
Registers a Ctrl+C handler that closes the connection. Off by default so the library never interferes with host signal handling.
Low-Level API¶
Dial¶
Create an SSH client connection directly:
Returns *ssh.Client for scenarios requiring more low-level control.
Argument Parsing¶
// Parse SSH arguments
config, cmd := sshpass.ParseSSHArgs([]string{"ssh", "user@host", "ls"})
// Parse SCP arguments
config, args := sshpass.ParseSCPArgs([]string{"scp", "file.txt", "user@host:/tmp/"})
// Parse Rsync arguments
config, args := sshpass.ParseRsyncArgs([]string{"rsync", "-avz", "./", "user@host:/backup/"})
// Detect command type
cmdType := sshpass.DetectCommandType(args)
Utility Functions¶
// Run SCP transfer
err := sshpass.RunSCP(client, args)
// Run Rsync transfer
err := sshpass.RunRsync(client, args)
// Clean remote path (handle Git Bash path conversion)
path, err := sshpass.CleanRemotePath("//tmp/file.txt")
// Parse user@host:path format
user, host, path := sshpass.ParseUserHostPath("user@host:/tmp/file.txt")
// Split paths (comma or space separated)
paths, err := sshpass.SplitPaths("a.txt,b.txt,c.txt", "local")
// Extract exit code from error
code, ok := sshpass.ExitCodeFromError(err)
Complete Examples¶
Batch Command Execution¶
package main
import (
"fmt"
"log"
sshpass "github.com/chuccp/win-sshpass"
)
func main() {
hosts := []string{"192.168.1.101", "192.168.1.102", "192.168.1.103"}
for _, host := range hosts {
cfg := sshpass.NewConfig()
cfg.Host = host
cfg.User = "root"
cfg.Password = "secret"
client, err := sshpass.NewClient(cfg)
if err != nil {
log.Printf("[%s] Connection failed: %v", host, err)
continue
}
fmt.Printf("[%s] Executing command...\n", host)
if err := client.Exec("uptime"); err != nil {
log.Printf("[%s] Command failed: %v", host, err)
}
client.Close()
}
}
File Upload with Progress¶
package main
import (
"fmt"
"log"
sshpass "github.com/chuccp/win-sshpass"
)
func main() {
cfg := sshpass.NewConfig()
cfg.Host = "example.com"
cfg.User = "root"
cfg.Password = "secret"
client, err := sshpass.NewClient(cfg,
sshpass.WithProgress(func(desc string, sent, total int64) {
pct := sent * 100 / total
fmt.Printf("\r%s %d%%", desc, pct)
}),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
sftp, err := client.SFTP()
if err != nil {
log.Fatal(err)
}
defer sftp.Close()
if err := sftp.Upload("./large-file.zip", "/tmp/large-file.zip"); err != nil {
log.Fatal(err)
}
fmt.Println("\nUpload complete!")
}
Next Steps¶
- Best Practices - Security and efficiency tips