Вопрос или проблема
Найдена хитрая ошибка в 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
на имени стандартного вывода, что может повлечь за собой следующие последствия:
-
Создание файла вместо использования существующего дескриптора: Программа теряет ссылку на стандартный поток вывода, и попытка читать из стандартного ввода может возвращать ошибку, так как STDIN теряет связь или изменяет своё состояние.
-
Ошибка
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 или при помощи утилиты, которая не переопределяет стандартные потоки).
-
Привилегии: Обратите внимание на уровень доступа пользователя. Некоторые операции с файловой системой требуют привилегий суперпользователя, поэтому тестирование в среде с ограниченными правами может предотвратить подобные ошибки.
Таким образом, ключом к решению вашей проблемы является осознание того, как работа с файловыми дескрипторами и вывода ошибок может повлиять на потоки ввода/вывода вашей программы и корректное обращение с ними.