Skip to content

Instantly share code, notes, and snippets.

@ayoubzulfiqar
Last active April 25, 2026 05:57
Show Gist options
  • Select an option

  • Save ayoubzulfiqar/542ae0ffac2ba1be8ffab7eaf3b42492 to your computer and use it in GitHub Desktop.

Select an option

Save ayoubzulfiqar/542ae0ffac2ba1be8ffab7eaf3b42492 to your computer and use it in GitHub Desktop.
READ ENV - without any package

Read ENV

Golang

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
)

var (
	envOnce sync.Once
	envErr  error
)

// loadEnv loads .env file once at application startup
func loadEnv() error {
	file, err := os.Open(".env")
	if err != nil {
		// Silent fail - .env is optional (production uses system env vars)
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("failed to open .env: %w", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	lineNum := 0
	for scanner.Scan() {
		lineNum++
		line := strings.TrimSpace(scanner.Text())
		
		// Skip empty lines and comments
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		// Split on first = only (values can contain =)
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			log.Printf("Warning: skipping malformed .env line %d: %s", lineNum, line)
			continue
		}

		key := strings.TrimSpace(parts[0])
		value := strings.TrimSpace(parts[1])

		// Remove surrounding quotes if present
		value = unquote(value)

		// Skip if key is empty
		if key == "" {
			continue
		}

		// Set in environment (Viper will pick this up automatically)
		if err := os.Setenv(key, value); err != nil {
			return fmt.Errorf("failed to set env var %s: %w", key, err)
		}
	}

	if err := scanner.Err(); err != nil {
		return fmt.Errorf("error reading .env: %w", err)
	}

	return nil
}

// unquote removes surrounding single or double quotes from a value
func unquote(s string) string {
	if len(s) >= 2 {
		if (s[0] == '"' && s[len(s)-1] == '"') ||
			(s[0] == '\'' && s[len(s)-1] == '\'') {
			return s[1 : len(s)-1]
		}
	}
	return s
}

// InitEnv initializes environment variables from .env file
// Call this ONCE at the start of main()
func InitEnv() {
	envOnce.Do(func() {
		envErr = loadEnv()
		if envErr != nil {
			log.Printf("Warning: .env loading failed: %v", envErr)
		}
	})
}

// GetEnv returns environment variable value (after .env is loaded)
// Use this instead of os.Getenv for consistency
func GetEnv(key, defaultValue string) string {
	InitEnv() // Ensures .env is loaded (no-op after first call)
	if val := os.Getenv(key); val != "" {
		return val
	}
	return defaultValue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment