package utils import ( "fmt" "os/exec" "strings" ) // ExecuteCommand runs a shell command and returns output func ExecuteCommand(name string, args ...string) (string, error) { cmd := exec.Command(name, args...) output, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("%s: %w", string(output), err) } return strings.TrimSpace(string(output)), nil } // ExecuteCommandSilent runs a command without capturing output (for fire-and-forget) func ExecuteCommandSilent(name string, args ...string) error { cmd := exec.Command(name, args...) return cmd.Run() } // ParseSize parses size string (e.g., "10G", "500M") and returns bytes func ParseSize(sizeStr string) (int64, error) { sizeStr = strings.ToUpper(strings.TrimSpace(sizeStr)) if sizeStr == "" { return 0, fmt.Errorf("empty size string") } var multiplier int64 = 1 lastChar := sizeStr[len(sizeStr)-1] if lastChar >= '0' && lastChar <= '9' { // No unit, assume bytes var size int64 fmt.Sscanf(sizeStr, "%d", &size) return size, nil } switch lastChar { case 'K': multiplier = 1024 case 'M': multiplier = 1024 * 1024 case 'G': multiplier = 1024 * 1024 * 1024 case 'T': multiplier = 1024 * 1024 * 1024 * 1024 default: return 0, fmt.Errorf("unknown size unit: %c", lastChar) } var size int64 _, err := fmt.Sscanf(sizeStr[:len(sizeStr)-1], "%d", &size) if err != nil { return 0, fmt.Errorf("invalid size format: %s", sizeStr) } return size * multiplier, nil } // FormatBytes formats bytes into human-readable string func FormatBytes(bytes int64) string { const unit = 1024 if bytes < unit { return fmt.Sprintf("%d B", bytes) } div, exp := int64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp]) }