diff --git a/internal/shell/commands.go b/internal/shell/commands.go deleted file mode 100644 index 80b8651..0000000 --- a/internal/shell/commands.go +++ /dev/null @@ -1,357 +0,0 @@ -// commands.go - встроенные команды fush shell (стиль busybox) -// Реализует базовые команды: exit, ls, cd, mkdir, rm, touch, pwd, cat, echo -// Все команды работают независимо от ОС, используя только стандартную библиотеку Go - -package shell - -import ( - "bufio" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "time" - - "fush/pkg/ansi" -) - -// cmdExit обрабатывает команду exit -func (s *Shell) cmdExit(args []string) error { - s.running.Store(false) - s.logger.Info("Выполнена команда exit") - return nil -} - -// cmdHelp обрабатывает команду help -func (s *Shell) cmdHelp(args []string) error { - fmt.Println() - ansi.Println(ansi.Cyan, "╔══════════════════════════════════════════════════════════════╗") - ansi.Println(ansi.Cyan, "║ fush shell - Доступные команды ║") - ansi.Println(ansi.Cyan, "╚══════════════════════════════════════════════════════════════╝") - fmt.Println() - - ansi.Println(ansi.Yellow, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - ansi.Println(ansi.BrightGreen, "ВСТРОЕННЫЕ КОМАНДЫ (BUSYBOX-STYLE):") - ansi.Println(ansi.Yellow, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - commands := []struct { - name string - desc string - }{ - {"exit", "Выход из оболочки"}, - {"help", "Показать эту справку"}, - {"ls [path]", "Вывести список файлов в директории"}, - {"cd [dir]", "Сменить текущую директорию"}, - {"pwd", "Показать текущую директорию"}, - {"mkdir [-p] ", "Создать новую директорию"}, - {"rm [-rf] ", "Удалить файл или директорию"}, - {"touch ", "Создать файл или обновить время доступа"}, - {"cat ", "Вывести содержимое файла"}, - {"echo [text...]", "Вывести текст на экран"}, - {"exec [args...]", "Выполнить внешнюю команду"}, - } - - for _, cmd := range commands { - fmt.Printf(" %-20s %s\n", ansi.Colorize(cmd.name, ansi.BrightWhite), cmd.desc) - } - - fmt.Println() - ansi.Println(ansi.Yellow, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - ansi.Println(ansi.BrightGreen, "ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ:") - ansi.Println(ansi.Yellow, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - fmt.Printf(" %-30s %s\n", ansi.Colorize("ls -la", ansi.BrightWhite), "Показать все файлы") - fmt.Printf(" %-30s %s\n", ansi.Colorize("cd /tmp", ansi.BrightWhite), "Перейти в /tmp") - fmt.Printf(" %-30s %s\n", ansi.Colorize("pwd", ansi.BrightWhite), "Показать текущий путь") - fmt.Printf(" %-30s %s\n", ansi.Colorize("mkdir -p a/b/c", ansi.BrightWhite), "Создать вложенные директории") - fmt.Printf(" %-30s %s\n", ansi.Colorize("rm -rf olddir", ansi.BrightWhite), "Удалить директорию рекурсивно") - fmt.Printf(" %-30s %s\n", ansi.Colorize("cat file.txt", ansi.BrightWhite), "Показать содержимое файла") - fmt.Printf(" %-30s %s\n", ansi.Colorize("echo Hello World", ansi.BrightWhite), "Вывести текст") - fmt.Printf(" %-30s %s\n", ansi.Colorize("exec go version", ansi.BrightWhite), "Выполнить внешнюю команду") - fmt.Printf(" %-30s %s\n", ansi.Colorize("ls | grep .go", ansi.BrightWhite), "Пайплайн") - fmt.Printf(" %-30s %s\n", ansi.Colorize("ls > files.txt", ansi.BrightWhite), "Перенаправление вывода") - - fmt.Println() - ansi.Println(ansi.Yellow, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - return nil -} - -// cmdLs обрабатывает команду ls (стиль busybox) -func (s *Shell) cmdLs(args []string) ([]byte, error) { - path := "." - showAll := false - longFormat := false - - // Парсим аргументы - for _, arg := range args { - if arg == "-a" || arg == "--all" { - showAll = true - } else if arg == "-l" { - longFormat = true - } else if !strings.HasPrefix(arg, "-") { - path = arg - } - } - - dir, err := os.Open(path) - if err != nil { - return nil, err - } - defer dir.Close() - - entries, err := dir.Readdir(-1) - if err != nil { - return nil, err - } - - var output strings.Builder - - for _, entry := range entries { - name := entry.Name() - - // Пропускаем скрытые файлы если не указан -a - if !showAll && strings.HasPrefix(name, ".") { - continue - } - - if longFormat { - // Формат: права ссылки владелец группа размер дата имя - perms := entry.Mode().String() - nlink := 1 // В Go сложно получить количество жестких ссылок - uid := fmt.Sprintf("%d", entry.Sys() != nil) // Упрощённо - gid := "users" - size := entry.Size() - modTime := entry.ModTime().Format("Jan _2 15:04") - - if entry.IsDir() { - name = name + "/" - } else if entry.Mode()&os.ModeSymlink != 0 { - // Для симлинков пытаемся прочитать цель - if target, err := os.Readlink(filepath.Join(path, entry.Name())); err == nil { - name = name + " -> " + target - } - } - - fmt.Fprintf(&output, "%s %3d %-8s %-8s %8d %s %s\n", - perms, nlink, uid, gid, size, modTime, name) - } else { - if entry.IsDir() { - name = name + "/" - } - fmt.Fprint(&output, name, "\n") - } - } - - return []byte(output.String()), nil -} - -// cmdCd обрабатывает команду cd -func (s *Shell) cmdCd(args []string) error { - path := s.GetEnv("HOME") - if len(args) > 0 { - path = args[0] - } - - if path == "~" { - path = s.GetEnv("HOME") - } - - if err := os.Chdir(path); err != nil { - return err - } - - pwd, err := os.Getwd() - if err == nil { - s.SetEnv("PWD", pwd) - } - - return nil -} - -// cmdPwd обрабатывает команду pwd -func (s *Shell) cmdPwd(args []string) ([]byte, error) { - dir, err := os.Getwd() - if err != nil { - return nil, err - } - return []byte(dir + "\n"), nil -} - -// cmdMkdir обрабатывает команду mkdir (с поддержкой -p) -func (s *Shell) cmdMkdir(args []string) error { - if len(args) == 0 { - return fmt.Errorf("требуется имя директории") - } - - createParents := false - var dirs []string - - for _, arg := range args { - if arg == "-p" || arg == "--parents" { - createParents = true - } else if !strings.HasPrefix(arg, "-") { - dirs = append(dirs, arg) - } - } - - for _, dir := range dirs { - var err error - if createParents { - err = os.MkdirAll(dir, 0755) - } else { - err = os.Mkdir(dir, 0755) - } - if err != nil { - return err - } - } - - return nil -} - -// cmdRm обрабатывает команду rm (с поддержкой -r и -f) -func (s *Shell) cmdRm(args []string) error { - if len(args) == 0 { - return fmt.Errorf("требуется имя файла") - } - - recursive := false - force := false - var targets []string - - for _, arg := range args { - switch arg { - case "-r", "-R", "--recursive": - recursive = true - case "-f", "--force": - force = true - default: - if !strings.HasPrefix(arg, "-") { - targets = append(targets, arg) - } - } - } - - for _, target := range targets { - info, err := os.Stat(target) - if err != nil { - if !force { - return err - } - continue - } - - if info.IsDir() && !recursive { - return fmt.Errorf("'%s' является директорией, используйте -r для удаления", target) - } - - if err := os.RemoveAll(target); err != nil && !force { - return err - } - } - - return nil -} - -// cmdTouch обрабатывает команду touch -func (s *Shell) cmdTouch(args []string) error { - if len(args) == 0 { - return fmt.Errorf("требуется имя файла") - } - - now := time.Now() - - for _, filename := range args { - if strings.HasPrefix(filename, "-") { - continue - } - - file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - file.Close() - - if err := os.Chtimes(filename, now, now); err != nil { - return err - } - } - - return nil -} - -// cmdCat обрабатывает команду cat (конкатенация файлов) -func (s *Shell) cmdCat(args []string) ([]byte, error) { - if len(args) == 0 { - // Читаем из stdin - info, err := os.Stdin.Stat() - if err != nil { - return nil, err - } - - if info.Mode()&os.ModeCharDevice != 0 { - return nil, fmt.Errorf("ожидается файл или stdin") - } - - reader := bufio.NewReader(os.Stdin) - var output strings.Builder - for { - line, err := reader.ReadString('\n') - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - output.WriteString(line) - } - return []byte(output.String()), nil - } - - var output strings.Builder - - for _, filename := range args { - if strings.HasPrefix(filename, "-") { - continue - } - - data, err := os.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("ошибка чтения '%s': %v", filename, err) - } - - output.Write(data) - if len(data) > 0 && data[len(data)-1] != '\n' { - output.WriteByte('\n') - } - } - - return []byte(output.String()), nil -} - -// cmdEcho обрабатывает команду echo -func (s *Shell) cmdEcho(args []string) ([]byte, error) { - newline := true - startIdx := 0 - - if len(args) > 0 && args[0] == "-n" { - newline = false - startIdx = 1 - } - - var output strings.Builder - for i := startIdx; i < len(args); i++ { - if i > startIdx { - output.WriteByte(' ') - } - output.WriteString(args[i]) - } - - if newline { - output.WriteByte('\n') - } - - return []byte(output.String()), nil -}