Introduction
Go provides powerful interfaces to interact with the operating system. This guide covers system calls, environment variables, and cross-platform programming. See Go Installation Guide, Go Ecosystem Overview, Go Best Practices for more context.
Understanding OS interfaces enables you to build system utilities, manage processes, and interact with system resources effectively.
Environment Variables
Basic Environment Operations
package main
import (
"fmt"
"os"
"strings"
)
// GetEnv gets an environment variable
func GetEnv(key string) string {
return os.Getenv(key)
}
// GetEnvOrDefault gets environment variable with default
func GetEnvOrDefault(key, defaultValue string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return defaultValue
}
// SetEnv sets an environment variable
func SetEnv(key, value string) error {
return os.Setenv(key, value)
}
// UnsetEnv unsets an environment variable
func UnsetEnv(key string) error {
return os.Unsetenv(key)
}
// GetAllEnv gets all environment variables
func GetAllEnv() map[string]string {
env := make(map[string]string)
for _, pair := range os.Environ() {
parts := strings.SplitN(pair, "=", 2)
if len(parts) == 2 {
env[parts[0]] = parts[1]
}
}
return env
}
// Example usage
func EnvironmentExample() {
// Get environment variable
home := GetEnv("HOME")
fmt.Printf("Home: %s\n", home)
// Get with default
debug := GetEnvOrDefault("DEBUG", "false")
fmt.Printf("Debug: %s\n", debug)
// Set environment variable
SetEnv("MY_VAR", "my_value")
// Get all environment variables
allEnv := GetAllEnv()
fmt.Printf("Total env vars: %d\n", len(allEnv))
}
Good: Proper OS Interface Implementation
package main
import (
"fmt"
"os"
"os/signal"
"os/user"
"path/filepath"
"runtime"
"syscall"
"time"
)
// OSInfo provides OS information
type OSInfo struct {
OS string
Arch string
Hostname string
User string
HomeDir string
TempDir string
}
// GetOSInfo gets OS information
func GetOSInfo() (*OSInfo, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
currentUser, err := user.Current()
if err != nil {
return nil, err
}
return &OSInfo{
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Hostname: hostname,
User: currentUser.Username,
HomeDir: currentUser.HomeDir,
TempDir: os.TempDir(),
}, nil
}
// ProcessInfo provides process information
type ProcessInfo struct {
PID int
PPID int
Args []string
Env map[string]string
WorkDir string
StartTime time.Time
}
// GetProcessInfo gets current process information
func GetProcessInfo() *ProcessInfo {
env := make(map[string]string)
for _, pair := range os.Environ() {
parts := strings.SplitN(pair, "=", 2)
if len(parts) == 2 {
env[parts[0]] = parts[1]
}
}
wd, _ := os.Getwd()
return &ProcessInfo{
PID: os.Getpid(),
PPID: os.Getppid(),
Args: os.Args,
Env: env,
WorkDir: wd,
}
}
// SignalHandler handles OS signals
type SignalHandler struct {
signals chan os.Signal
done chan bool
}
// NewSignalHandler creates a new signal handler
func NewSignalHandler() *SignalHandler {
return &SignalHandler{
signals: make(chan os.Signal, 1),
done: make(chan bool),
}
}
// Handle handles signals
func (sh *SignalHandler) Handle(callback func(os.Signal)) {
signal.Notify(sh.signals, syscall.SIGINT, syscall.SIGTERM)
go func() {
for {
select {
case sig := <-sh.signals:
callback(sig)
case <-sh.done:
return
}
}
}()
}
// Stop stops signal handling
func (sh *SignalHandler) Stop() {
signal.Stop(sh.signals)
close(sh.done)
}
// FilePermissions manages file permissions
type FilePermissions struct {
path string
}
// NewFilePermissions creates new file permissions manager
func NewFilePermissions(path string) *FilePermissions {
return &FilePermissions{path: path}
}
// GetPermissions gets file permissions
func (fp *FilePermissions) GetPermissions() (os.FileMode, error) {
info, err := os.Stat(fp.path)
if err != nil {
return 0, err
}
return info.Mode().Perm(), nil
}
// SetPermissions sets file permissions
func (fp *FilePermissions) SetPermissions(mode os.FileMode) error {
return os.Chmod(fp.path, mode)
}
// IsReadable checks if file is readable
func (fp *FilePermissions) IsReadable() bool {
_, err := os.Open(fp.path)
return err == nil
}
// IsWritable checks if file is writable
func (fp *FilePermissions) IsWritable() bool {
info, err := os.Stat(fp.path)
if err != nil {
return false
}
return info.Mode().Perm()&0200 != 0
}
// IsExecutable checks if file is executable
func (fp *FilePermissions) IsExecutable() bool {
info, err := os.Stat(fp.path)
if err != nil {
return false
}
return info.Mode().Perm()&0111 != 0
}
// Example usage
func OSInterfaceExample() {
// Get OS info
osInfo, _ := GetOSInfo()
fmt.Printf("OS: %s/%s\n", osInfo.OS, osInfo.Arch)
fmt.Printf("User: %s\n", osInfo.User)
// Get process info
procInfo := GetProcessInfo()
fmt.Printf("PID: %d\n", procInfo.PID)
// Handle signals
handler := NewSignalHandler()
handler.Handle(func(sig os.Signal) {
fmt.Printf("Received signal: %v\n", sig)
})
// Check file permissions
perms := NewFilePermissions("test.txt")
readable := perms.IsReadable()
fmt.Printf("Readable: %v\n", readable)
}
Bad: Improper OS Interface Usage
package main
// BAD: No error handling
func BadGetEnv(key string) string {
return os.Getenv(key) // Could be empty
}
// BAD: No signal handling
func BadSignalHandling() {
// No signal handling
// Process can't be interrupted gracefully
}
// BAD: No permission checking
func BadFileAccess(path string) {
// No permission checks
// Could fail at runtime
}
Problems:
- No error handling
- No signal handling
- No permission checking
- No cross-platform consideration
Cross-Platform Programming
package main
import (
"os"
"path/filepath"
"runtime"
)
// GetConfigDir gets platform-specific config directory
func GetConfigDir() string {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "MyApp")
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".config", "myapp")
}
// GetDataDir gets platform-specific data directory
func GetDataDir() string {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "MyApp", "Data")
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".local", "share", "myapp")
}
// GetLogDir gets platform-specific log directory
func GetLogDir() string {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "MyApp", "Logs")
}
return "/var/log/myapp"
}
// GetExecutablePath gets executable path
func GetExecutablePath() (string, error) {
return os.Executable()
}
// GetWorkingDirectory gets working directory
func GetWorkingDirectory() (string, error) {
return os.Getwd()
}
// ChangeWorkingDirectory changes working directory
func ChangeWorkingDirectory(path string) error {
return os.Chdir(path)
}
Best Practices
1. Always Check Errors
if err := os.Setenv(key, value); err != nil {
return err
}
2. Handle Signals Gracefully
handler := NewSignalHandler()
handler.Handle(func(sig os.Signal) {
// Cleanup
})
3. Use Cross-Platform Paths
path := filepath.Join(homeDir, ".config", "app")
4. Check Permissions
perms := NewFilePermissions(path)
if !perms.IsReadable() {
return fmt.Errorf("file not readable")
}
Common Pitfalls
1. No Error Handling
Always check errors from OS operations.
2. Platform Assumptions
Don’t assume Unix-like environment.
3. No Signal Handling
Handle signals for graceful shutdown.
4. Hardcoded Paths
Use platform-specific path functions.
Resources
Summary
Proper OS interface usage is essential. Key takeaways:
- Handle environment variables safely
- Implement signal handling
- Check file permissions
- Use cross-platform paths
- Handle errors properly
- Support multiple platforms
- Test on target platforms
By mastering OS interfaces, you can build robust system utilities.
Comments