2012-02-25 23 views
10

Làm cách nào để xác định 'catchOutput' để chỉ chạy các kết quả đầu ra chính 'thanh'?Bắt giữ/lấy cắp dữ liệu stdout trong haskell

Tức là, làm cách nào tôi có thể truy cập cả luồng đầu ra (đầu ra) và đầu ra thực tế của tác vụ io riêng biệt?

catchOutput :: IO a -> IO (a,String) 
catchOutput = undefined 

doSomethingWithOutput :: IO a -> IO() 
doSomethingWithOutput io = do 
    (_ioOutp, stdOutp) <- catchOutput io 
    if stdOutp == "foo" 
     then putStrLn "bar" 
     else putStrLn "fail!" 

main = doSomethingWithOutput (putStr "foo") 

tốt nhất giả thuyết "giải pháp" tôi đã tìm thấy cho đến nay bao gồm chuyển hướng stdout, inspired by this, để một dòng tập tin và sau đó đọc từ tập tin đó (Bên cạnh đó là siêu xấu xí tôi đã không có khả năng đọc trực tiếp sau khi viết từ một tập tin Có thể tạo ra một "dòng đệm tùy chỉnh" mà không cần phải lưu trữ trong một tập tin?). Mặc dù cảm thấy 'hơi' giống như một ca khúc phụ.

Một góc khác có vẻ như sử dụng 'hGetContents stdout' nếu điều đó được cho là làm những gì tôi nghĩ. Nhưng tôi không được phép đọc từ stdout. Mặc dù googling nó dường như cho thấy rằng nó đã được used.

+0

Tùy thuộc vào trường hợp sử dụng của bạn và liệu tính di động có phải là vấn đề bạn có thể sử dụng thư viện thử nghiệm này không: https://hackage.haskell.org/package/system-posix-redirect-1.1.0.1/docs/System-Posix -Redirect.html –

Trả lời

4

Tại sao không chỉ sử dụng một người viết đơn thuần thay thế? Ví dụ,

import Control.Monad.Writer 

doSomethingWithOutput :: WriterT String IO a -> IO() 
doSomethingWithOutput io = do 
    (_, res) <- runWriterT io 
    if res == "foo" 
     then putStrLn "bar" 
     else putStrLn "fail!" 

main = doSomethingWithOutput (tell "foo") 

Ngoài ra, bạn có thể sửa đổi hành động bên trong của bạn để có một Handle để viết thư cho thay vì stdout. Sau đó bạn có thể sử dụng một cái gì đó như knob để tạo một bộ xử lý trong bộ nhớ mà bạn có thể chuyển đến hành động bên trong và kiểm tra nội dung của nó sau đó.

+0

Thing là, tôi không thể thực sự làm nhiều về đối số io trong mã thực của tôi. Điều đó sẽ yêu cầu thiết kế lại hành vi bên ngoài. "Ứng dụng khách" được cho là gửi/chuyển các hành động io (làm đối số). Vì vậy, tại một số điểm hay cách khác, tôi sẽ phải sử dụng một cái gì đó như 'catchOutput'. Vì vậy, các nhà văn monad là không thực sự áp dụng cho vấn đề của tôi .. Mặt khác có vẻ hữu ích. :) – worldsayshi

+1

@worldsayshi Bạn có vẻ hiểu lầm ý kiến ​​của hammar. Lưu ý rằng anh ta không sử dụng đơn vị 'Writer', nhưng chỉ là một trình soạn thảo' WriterT a IO' - vì vậy bạn vẫn có thể gọi hành động 'IO' của bạn bằng cách sử dụng' liftIO io'. –

+0

Tôi đã chỉnh sửa mã của mình, xin lỗi về điều đó. Có lẽ một số nhầm lẫn bởi điều đó.Những gì tôi đang tìm kiếm đầu tiên là văn bản kết thúc trong stdout. Đó là những gì _now_ 'res', trong ví dụ của tôi. Tôi không thấy làm thế nào liftIO có thể cho tôi truy cập vào đó. Sau đó, tôi cũng muốn ouput từ thực sự chạy các hành động io là tốt. Đó là hiện tại đầu ra bị bỏ qua từ 'catchOutput'. Nếu tôi nhận được một 'IO()' làm đối số nâng nó sẽ cho tôi một 'm()'. – worldsayshi

1

Như @hammar chỉ ra, bạn có thể sử dụng một núm để tạo ra một tập tin trong bộ nhớ, nhưng bạn cũng có thể sử dụng hDuplicatehDuplicateTo thay đổi stdout đến tập tin bộ nhớ, và ngược lại. Nội dung nào đó giống như mã hoàn toàn chưa được kiểm tra sau đây:

catchOutput io = do 
    knob <- newKnob (pack []) 
    let before = do 
     h <- newFileHandle knob "<stdout>" WriteMode 
     stdout' <- hDuplicate stdout 
     hDuplicateTo h stdout 
     hClose h 
     return stdout' 
     after stdout' = do 
     hDuplicateTo stdout' stdout 
     hClose stdout' 
    a <- bracket_ before after io 
    bytes <- Data.Knob.getContents knob 
    return (a, unpack bytes) 
+3

Hmm, bây giờ tôi đã cài đặt 'Data.Knob', tôi không thể sử dụng phương thức này. Lệnh gọi 'hDuplicateTo' không thành công khi cố gắng nhân đôi núm điều khiển thành' stdout', với lỗi sau: 'Wots: : hDuplicateTo: hoạt động bất hợp pháp (các chốt không tương thích)'. – pat

3

Tôi đã sử dụng chức năng sau để kiểm tra đơn vị chức năng in thành giá trị xuất chuẩn.

import GHC.IO.Handle 
import System.IO 
import System.Directory 

catchOutput :: IO() -> IO String 
catchOutput f = do 
    tmpd <- getTemporaryDirectory 
    (tmpf, tmph) <- openTempFile tmpd "haskell_stdout" 
    stdout_dup <- hDuplicate stdout 
    hDuplicateTo tmph stdout 
    hClose tmph 
    f 
    hDuplicateTo stdout_dup stdout 
    str <- readFile tmpf 
    removeFile tmpf 
    return str 

Tôi không chắc về cách tiếp cận tệp trong bộ nhớ, nhưng nó hoạt động tốt cho một lượng nhỏ đầu ra với một tệp tạm thời.

4

Có một số gói trên Hackage hứa hẹn sẽ thực hiện điều đó: io-capture và im lặng. âm thầm dường như được duy trì và hoạt động trên Windows quá (io-capture chỉ hoạt động trên Unix). Với âm thầm, bạn sử dụng chụp:

import System.IO.Silently 

main = do 
    (output, _) <- capture $ putStr "hello" 
    putStrLn $ output ++ " world" 

Lưu ý rằng nó hoạt động bằng cách chuyển hướng đầu ra sang tệp tạm thời và sau đó đọc ... Nhưng miễn là nó hoạt động!

Các vấn đề liên quan