Автоматизация рутины на Go: как писать скрипты, которые работают, а не ломаются

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Go — идеальный язык для автоматизации?

Вы когда-нибудь писали скрипт на Python, который отлично работал на вашем ноутбуке, но «падал» на сервере из-за отсутствия нужной версии интерпретатора? Или, может быть, ваш Bash-скрипт превращался в неподдерживаемого монстра, когда логика становилась чуть сложнее, чем «скопировать файл»?

Если да, то вам стоит присмотреться к Go. Этот язык был создан в недрах Google именно для решения проблем, с которыми сталкиваются инженеры при написании утилит и сервисов. Он компилируется в один бинарник (никаких зависимостей!), работает с космической скоростью и имеет простой, но мощный синтаксис.

В этой статье мы не будем учить вас основам синтаксиса. Мы сразу перейдем к делу: автоматизация задач на Go с реальными примерами кода. Вы узнаете, как написать свои первые «Go-скрипты», которые будут работать надежнее и быстрее их Python-аналогов. Поехали!

1. Работа с файловой системой: чистим логи как профи

Одна из самых частых задач админа или разработчика — чистка старых логов. Напишем утилиту, которая удаляет файлы старше N дней в заданной директории. Это классический пример автоматизации на Go.

package main

import (
    "fmt"
    "os"
    "path/filepath"
    "time"
)

func main() {
    // Путь к директории с логами
    logDir := "/var/log/myapp"
    // Удаляем файлы старше 7 дней
    maxAge := 7 * 24 * time.Hour

    err := filepath.Walk(logDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // Пропускаем директории
        if info.IsDir() {
            return nil
        }

        // Проверяем возраст файла
        if time.Since(info.ModTime()) > maxAge {
            fmt.Printf("Удаляем старый файл: %s (возраст: %v)\n", path, time.Since(info.ModTime()))
            err := os.Remove(path)
            if err != nil {
                fmt.Printf("Ошибка удаления %s: %v\n", path, err)
            }
        }
        return nil
    })

    if err != nil {
        fmt.Printf("Ошибка обхода директории: %v\n", err)
    }
}

Этот код — отличный пример того, как автоматизировать задачи на Go без лишних библиотек. Встроенный пакет filepath рекурсивно обходит папки, а os.Remove очищает пространство. Скомпилировав его один раз, вы получаете бинарник, который можно запускать по cron на любом сервере.

2. HTTP-запросы и парсинг API

Допустим, вам нужно мониторить статус нескольких микросервисов или парсить данные с внешнего API. Go с его горутинами справляется с этим блестяще. Напишем скрипт, который проверяет доступность сайтов и отправляет уведомление, если что-то упало. Это еще один мощный пример автоматизации на Golang.

package main

import (
    "fmt"
    "net/http"
    "sync"
    "time"
)

func checkURL(url string, wg *sync.WaitGroup) {
    defer wg.Done()

    client := http.Client{
        Timeout: 5 * time.Second,
    }

    resp, err := client.Get(url)
    if err != nil {
        fmt.Printf("[FAIL] %s - %v\n", url, err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        fmt.Printf("[WARN] %s - статус: %d\n", url, resp.StatusCode)
    } else {
        fmt.Printf("[OK]   %s - работает\n", url)
    }
}

func main() {
    urls := []string{
        "https://google.com",
        "https://github.com",
        "https://pythonlib.ru",
        "https://non-existent-site.xyz",
    }

    var wg sync.WaitGroup

    for _, url := range urls {
        wg.Add(1)
        go checkURL(url, &wg) // Запускаем в горутине
    }

    wg.Wait() // Ждем завершения всех горутин
    fmt.Println("Проверка завершена.")
}

Обратите внимание на sync.WaitGroup. Это ключевой паттерн в Go для ожидания завершения параллельных задач. Благодаря горутинам, ваш скрипт мониторинга проверит 100 сайтов быстрее, чем Python-скрипт проверит 10.

3. Парсинг логов в реальном времени

Иногда нужно не просто чистить логи, а анализировать их. Например, найти все IP-адреса из определенной подсети или подсчитать количество ошибок. Go идеален для парсинга логов благодаря своей производительности.

Предположим, у нас есть файл access.log стандартного формата Nginx. Мы хотим вывести все уникальные IP-адреса, с которых было больше 10 запросов.

package main

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

func main() {
    file, err := os.Open("access.log")
    if err != nil {
        fmt.Println("Ошибка открытия файла:", err)
        return
    }
    defer file.Close()

    ipCount := make(map[string]int)

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()
        // Первое поле в логе — это IP
        parts := strings.Fields(line)
        if len(parts) > 0 {
            ip := parts[0]
            ipCount[ip]++
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Println("Ошибка чтения:", err)
        return
    }

    fmt.Println("IP-адреса с запросами > 10:")
    for ip, count := range ipCount {
        if count > 10 {
            fmt.Printf("%s - %d запросов\n", ip, count)
        }
    }
}

Этот скрипт обрабатывает гигабайтные логи за секунды. Автоматизация задач на Go в данном случае позволяет вам не писать сложные однострочники на awk, а иметь читаемый и поддерживаемый код.

4. Отправка email-уведомлений

Крайне полезная задача — отправка писем. Допустим, вы написали скрипт бэкапа (на Go, конечно) и хотите получать отчет о его выполнении. В Go есть встроенная поддержка SMTP.

package main

import (
    "fmt"
    "net/smtp"
    "os"
)

func sendEmail(to, subject, body string) error {
    from := "your-email@gmail.com"
    password := "your-app-password" // Используйте пароль приложения!

    smtpHost := "smtp.gmail.com"
    smtpPort := "587"

    msg := []byte("To: " + to + "\r\n" +
        "Subject: " + subject + "\r\n" +
        "MIME-version: 1.0;\r\n" +
        "Content-Type: text/plain; charset=\"UTF-8\";\r\n" +
        "\r\n" +
        body)

    auth := smtp.PlainAuth("", from, password, smtpHost)

    err := smtp.SendMail(smtpHost+":"+smtpPort, auth, from, []string{to}, msg)
    if err != nil                

Блоги

Рекомендаций по книгам