Compare commits

...

3 Commits

3 changed files with 66 additions and 23 deletions

5
go.mod
View File

@@ -4,4 +4,7 @@ go 1.24.3
require github.com/fsnotify/fsnotify v1.9.0 require github.com/fsnotify/fsnotify v1.9.0
require golang.org/x/sys v0.13.0 // indirect require (
github.com/adrg/xdg v0.5.3
golang.org/x/sys v0.26.0 // indirect
)

14
go.sum
View File

@@ -1,4 +1,14 @@
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

70
main.go
View File

@@ -2,7 +2,9 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io/fs"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@@ -12,6 +14,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/adrg/xdg"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
@@ -22,6 +25,11 @@ func main() {
destDir := "/home/av/temp/dest" destDir := "/home/av/temp/dest"
os.MkdirAll(destDir, 0755) os.MkdirAll(destDir, 0755)
counterFile, err := xdg.DataFile("filemover/counter")
if err != nil {
log.Fatalf("Application data dir not accessible, %v", err)
}
// Контекст для graceful shutdown // Контекст для graceful shutdown
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -36,20 +44,20 @@ func main() {
// Запуск единственного обработчика // Запуск единственного обработчика
go func() { go func() {
defer wg.Done() defer wg.Done()
counter := loadCounter(destDir) counter := loadCounter(counterFile)
for { for {
select { select {
case file, ok := <-tasks: case file, ok := <-tasks:
if !ok { if !ok {
// Канал закрыт, завершаем работу // Канал закрыт, завершаем работу
saveCounter(destDir, counter) saveCounter(counterFile, counter)
log.Println("Worker stopped") log.Println("Worker stopped")
return return
} }
processFile(file, destDir, &counter) processFile(file, destDir, counterFile, &counter)
case <-ctx.Done(): case <-ctx.Done():
// Получен сигнал завершения // Получен сигнал завершения
saveCounter(destDir, counter) saveCounter(counterFile, counter)
log.Println("Worker stopped by context") log.Println("Worker stopped by context")
return return
} }
@@ -117,7 +125,7 @@ mainLoop:
log.Println("Shutdown complete") log.Println("Shutdown complete")
} }
func processFile(filePath, destDir string, counter *int) { func processFile(filePath, destDir, counterPath string, counter *int) {
// Проверка что это файл // Проверка что это файл
info, err := os.Stat(filePath) info, err := os.Stat(filePath)
if err != nil { if err != nil {
@@ -131,22 +139,45 @@ func processFile(filePath, destDir string, counter *int) {
// Ожидание завершения записи // Ожидание завершения записи
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
// Копирование moveAttempt := 0
newName := fmt.Sprintf("%03d", *counter)
destPath := filepath.Join(destDir, newName)
if err := os.Rename(filePath, destPath); err != nil {
log.Printf("Moving failed: %v", err)
return
}
// Обновление счетчика for {
*counter++ if moveAttempt > 100 {
saveCounter(destDir, *counter) log.Printf("Moving failed after %d attempts, see messages", moveAttempt)
log.Printf("Moved: %s -> %s", filePath, destPath) break
}
*counter++
saveCounter(counterPath, *counter)
newName := fmt.Sprintf("%05d%s", *counter, filepath.Ext(filePath))
destPath := filepath.Join(destDir, newName)
_, err := os.Stat(destPath)
if err == nil {
log.Printf("Moving failed, file already exists: %s", destPath)
moveAttempt++
continue
}
if !errors.Is(err, fs.ErrNotExist) {
log.Printf("Moving failed: %v", err)
moveAttempt++
continue
}
if err := os.Rename(filePath, destPath); err != nil {
log.Printf("Moving failed: %v", err)
moveAttempt++
continue
}
log.Printf("Moved: %s -> %s", filePath, destPath)
break
}
} }
func loadCounter(dir string) int { func loadCounter(counterPath string) int {
counterPath := filepath.Join(dir, "counter.txt")
data, err := os.ReadFile(counterPath) data, err := os.ReadFile(counterPath)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
@@ -162,8 +193,7 @@ func loadCounter(dir string) int {
return count return count
} }
func saveCounter(dir string, count int) { func saveCounter(counterPath string, count int) {
counterPath := filepath.Join(dir, "counter.txt")
if err := os.WriteFile(counterPath, []byte(strconv.Itoa(count)), 0644); err != nil { if err := os.WriteFile(counterPath, []byte(strconv.Itoa(count)), 0644); err != nil {
log.Printf("Failed to save counter: %v", err) log.Printf("Failed to save counter: %v", err)
} }