Warum gibt mein Programm einen Fehler zurück, der besagt, dass die Datei nicht existiert, wenn dies der Fall ist?


Sam Wood

Ich schreibe die Grundlagen eines benutzerdefinierten Planungstools, das eine Konfigurationsdatei für "Jobs" liest und sie dem Zeitplan hinzufügt, um sie regelmäßig auszuführen. Es ist im Moment sehr einfach, wie ein Proof of Concept vor dem Refactoring und einige zusätzliche erweiterte Funktionen.

Wenn ich versuche, dieses Programm auszuführen, meldet es, dass das Skript nicht gefunden wurde. Das Skript "test1.sh" existiert in dem Pfad, von dem aus ich es ausführen möchte, und es hat Ausführungsberechtigungen.

Ich erhalte den folgenden Fehler, kann ihn aber nicht erklären oder herausfinden, da das Skript unter dem Pfad existiert, in dem ich es ausführe:

-> ./scheduler-example
2021/08/16 12:48:54 fork/exec /Users/me/scheduler-example/scripts/test1.sh: no such file or directory

Der Planer-Code:

package main

import (
    "io"
    "log"
    "os"
    "os/exec"
    "path/filepath"
    "time"

    "github.com/go-co-op/gocron"
    "gopkg.in/yaml.v3"
)

type Config struct {
    GlobalLog string `yaml:"GlobalLog"`
    Jobs      []Job  `yaml:"Jobs"`
}

type Job struct {
    Name      string `yaml:"Name"`
    Command   string `yaml:"Command"`
    Frequency string `yaml:"Frequency"`
    Tag       string `yaml:"Tag,omitempty"`
    Log       string `yaml:"Log,omitempty"`
}

const ROOT string = "/Users/me/scheduler-example"

func main() {
    // STEP1: Parse the config file
    configFile := filepath.Join(ROOT, "config", "scheduler-example.yaml")
    f, err := os.Open(configFile)
    if err != nil {
        log.Fatalln(err)
    }

    configData, err := io.ReadAll(f)
    if err != nil {
        log.Fatalln(err)
    }

    c := Config{}
    err = yaml.Unmarshal(configData, &c)
    if err != nil {
        log.Fatalln(err)
    }

    // STEP2: Validate the config
    if c.GlobalLog == "" {
        log.Fatalln("Global log not defined")
    }
    if c.Jobs == nil {
        log.Fatalln("No jobs defined")
    }
    for _, j := range c.Jobs {
        if j.Name == "" {
            log.Fatalln("Job name not defined")
        }
        if j.Command == "" {
            log.Fatalln("Job command not defined")
        }
        if j.Frequency == "" {
            log.Fatalln("Job frequency not defined")
        }
    }

    // STEP3: Create the scheduler and add jobs
    s := gocron.NewScheduler(time.UTC)

    for _, j := range c.Jobs {
        script := filepath.Join(ROOT, "scripts", j.Command)
        cmd := exec.Command(script)
        cmdLog := filepath.Join(ROOT, "logs", j.Log)
        l, err := os.OpenFile(cmdLog, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
        if err != nil {
            log.Fatalln(err)
        }
        cmd.Stdout = l
        cmd.Stderr = l
        freq := j.Frequency
        tag := j.Tag
        s.Every(freq).Tag(tag).Do(func() {
            err = cmd.Run()
            if err != nil {
                log.Fatalln(err)
            }
        })
    }

    // STEP4: Run the scheduler
    s.StartBlocking()
}

Konfigurationsdatei:

GlobalLog: /tmp/scheduler-example.log
Jobs:
  - Name: test1
    Command: test1.sh
    Frequency: 5s
    Tag: test1
    Log: test1.log
  - Name: test2
    Command: test2.sh
    Frequency: 5s
    Tag: test2
    Log: test2.log

Verzeichnisaufbau:

-> tree .
.
├── config
│   └── scheduler-example.yaml
├── go.mod
├── go.sum
├── logs
│   ├── test1.log
│   └── test2.log
├── scheduler-example.go
└── scripts
    ├── test1.sh
    └── test2.sh

3 directories, 8 files

Das test1.sh-Skript:

#!/bin/env bash

for i in {1..100}; do
    echo i
    sleep 10
done

Danke für jede Hilfe!

OZahed

laut Go-Dokumenten

Package Exec führt externe Befehle aus. Es umschließt os.StartProcess, um es einfacher zu machen, stdin und stdout neu zuzuordnen, I/O mit Pipes zu verbinden und andere Anpassungen vorzunehmen.

Es startet einen isolierten OS-Prozess. Was Sie brauchen, ist eine Terminalsitzung oder ein direkter Bash-Aufruf.

Unter Linux bedeutet es beim Aufrufen ./script.sheigentlich /bin/sh script.sh. ./script.shfunktioniert, weil das Terminal (oder CMD für .bat-Dateien unter Windows) die Datei zeilenweise als reine Bash-Befehle behandelt. um benutzerdefinierte Bash-Dateien auszuführen, die wir verwenden können

cmd := exec.Command("/bin/sh",script)

oder

cmd := exec.Command("bash",script)

Verwandte Artikel