Как я могу комбинировать 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.