Восстановление побочных эффектов STDOUT для STDIN.

Вопрос или проблема

Найдена хитрая ошибка в Golang. Модификация STDOUT делает чтение из STDIN невозможным:

package main

import "os"

func main() {
        bts := make([]byte, 10)

        // Побочные эффекты
        os.Create(os.Stdout.Name()) // закомментируйте, чтобы исправить чтение из STDIN

        n, err := os.Stdin.Read(bts)
        if err != nil {
           println("err: " + err.Error()) // err: read /dev/stdin: resource temporarily unavailable
           return
        }

        println(n)
}

// Результат

err: read /dev/stdin: resource temporarily unavailable

// Ожидалось

(без ошибок)

Все работает прекрасно… Я на Ubuntu 24.04 с версией go go1.23.1

Когда я выполняю ваш код, он правильно ждет ввода из stdin… в том же терминале, когда я ввожу

happyhalloween

и нажимаю Enter, он отвечает выводом

10

затем в том же терминале появляется

ween

Команда ‘ween’ не найдена, вы имели в виду:

Выше указано правильно, поскольку вы выделяете 10 байт, которые затем используются, и код завершается… лишний ввод не читается кодом и просто выводится в тот же терминал, где сообщается, что 'ween' не найден

так что всё в порядке… Пожалуйста, обновите ваш вопрос, чтобы указать, какую ОС вы используете… ваш сетап как-то перенаправляет stdin в другое место? Возможно, родительский процесс, из которого выполняется ваш код, перенаправляет stdin… Запускается ли ваш код через supervisord или аналогичный процесс запуска и наблюдения кода?

PS всегда проверяйте на наличие ошибок… ваш вызов os.Create должен проверять ошибку… здесь ошибка отсутствует, так что проблема в другом месте

Это не ошибка в Go. Ваш код повреждает окружение, в котором работает ваша программа, в данном случае специальный файл /dev/stdin, выполняя os.Create() на нем. (Вы, вероятно, запускаете свой код от имени привилегированного пользователя, так как обычным пользователям не следует иметь возможность вмешиваться в /dev/.) Большинство языковых сред не предотвращают повреждение окружения; это ваша ответственность как программиста. Вы также можете выполнить os.RemoveAll("/") и удалить все системные файлы, это тоже не будет ошибкой.

Ответ или решение

Вопрос, описанный вами, действительно касается манипуляции с файловыми дескрипторами в Go и особенностей работы операционной системы. Основная проблема, с которой вы столкнулись, заключается в несанкционированном изменении состояния стандартного ввода (STDIN) за счет создания нового файла на месте стандартного вывода (STDOUT).

Объяснение проблемы

Когда вы вызываете os.Create(os.Stdout.Name()), вы фактически пытаетесь создать файл с именем, используемым для STDOUT. В Unix-подобных системах STDOUT ссылается на специальные файлы, такие как /dev/stdout, и изменение их состояния может нарушить работу других стандартных потоков, таких как STDIN.

Неправильное использование os.Create

Ваш код вызывает os.Create на имени стандартного вывода, что может повлечь за собой следующие последствия:

  1. Создание файла вместо использования существующего дескриптора: Программа теряет ссылку на стандартный поток вывода, и попытка читать из стандартного ввода может возвращать ошибку, так как STDIN теряет связь или изменяет своё состояние.

  2. Ошибка resource temporarily unavailable: Это сообщение об ошибке указывает на то, что дескриптор ввода больше не доступен для чтения, поскольку были произведены изменения.

Как решить проблему

Для решения проблемы, вам следует избегать манипуляции с именами стандартных потоков (STDIN, STDOUT, STDERR). Не используйте os.Create для создания объектов на этих именах. Перепишите вашу программу следующим образом, чтобы избежать внесения изменений в специальное окружение:

package main

import (
    "fmt"
    "os"
)

func main() {
    bts := make([]byte, 10)

    // Удаляем или комментируем данную строку, чтобы избежать побочных эффектов
    // os.Create(os.Stdout.Name()) // Не выполняйте эту операцию

    n, err := os.Stdin.Read(bts)
    if err != nil {
        fmt.Println("err: " + err.Error()) // Обработка ошибки
        return
    }

    fmt.Println(n) // Отображение количества прочитанных байтов
}

Дополнительные рекомендации

  • Проверяйте ошибки: Убедитесь, что вы всегда проверяете возвращаемые ошибки от функций, особенно влияющих на файловую систему и потоки.

  • Дебаггинг и окружение: Если вы продолжаете сталкиваться с проблемами, попробуйте запустить вашу программу в другом терминале или с использованием другого метода запуска (например, в IDE или при помощи утилиты, которая не переопределяет стандартные потоки).

  • Привилегии: Обратите внимание на уровень доступа пользователя. Некоторые операции с файловой системой требуют привилегий суперпользователя, поэтому тестирование в среде с ограниченными правами может предотвратить подобные ошибки.

Таким образом, ключом к решению вашей проблемы является осознание того, как работа с файловыми дескрипторами и вывода ошибок может повлиять на потоки ввода/вывода вашей программы и корректное обращение с ними.

Оцените материал
Добавить комментарий

Капча загружается...