Как я могу комбинировать Handles в Haskell?
Я хотел бы иметь что-то вроде bash 2>&1
redirect в Haskell, который объединяет stdout
и stderr
из процесса в один Handle
. Было бы неплохо сделать это непосредственно с помощью System.Process.createProcess
или аналогичной библиотечной функции, особенно если она использовала ту же семантику, что и bash redirect w.r.t. перемежающий ввод из ручек.
Гибкость, предлагаемая createProcess
, кажется перспективной вначале: можно указать Handle
для стандартных дескрипторов файлов, поэтому для stdout
и stderr
можно указать тот же Handle
. Однако аргументы Handle
должны существовать до вызова. Без возможности создания Handle
из тонкого воздуха перед вызовом функции я не уверен, что проблема может быть решена таким образом.
Изменить: Решение должно работать независимо от платформы.
Ответы
Ответ 1
Из здесь:
import GHC.IO.Handle -- yes, it GHC-specific
import System.IO
main = do
stdout_excl <- hDuplicate stdout
hDuplicateTo stderr stdout -- redirect stdout to stderr
putStrLn "Hello stderr" -- will print to stderr
hPutStrLn stdout_excl "Hello stdout" -- prints to stdout
Ответ 2
Получение ваших рук Handle
не слишком сложно: System.IO
предлагает константы stdin,stdout,stderr :: Handle
и функции withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
и openFile :: FilePath -> IOMode -> IO Handle
.
В качестве альтернативы вы можете запрашивать новые каналы из createProcess
и настраивать себя как службу пересылки (чтение из новых stdout
и stderr
дескрипторов вашего ребенка и отправка обоих туда, где вам нравится).
Ответ 3
Так как Windows поддерживает pipe (3)
изначально и библиотека GHC IO использует CRT файловые дескрипторы внутри Windows, возможно придумать решение, которое работает как на Windows, так и на * nix по крайней мере. Основной алгоритм:
- Вызов
pipe
. Это дает вам два дескриптора файла, которые вы можете преобразовать в Handles
с помощью fdToHandle'
.
- Вызовите
createProcess
с stdout
и stderr
, оба установлены в конец записи в трубку.
- Прочитайте выходные данные на выходе из канала чтения.
Модуль System.Posix.Internals
, который экспортирует c_pipe
, по умолчанию скрыт, но вы можете скомпилировать пользовательскую версию base
который позволяет вам получить доступ к нему (совет: используйте cabal-dev). Кроме того, вы можете получить доступ к pipe
через FFI. NB: это решение является специфическим для GHC.