2012-02-25 15 views
10

में stdout को पकड़ना/अपहरण करना मैं 'कैचऑटपुट' को कैसे परिभाषित कर सकता हूं ताकि मुख्य आउटपुट केवल 'बार' चल रहा हो?हैकेल

यही है, मैं आउटपुट स्ट्रीम (stdout) और io कार्रवाई के वास्तविक आउटपुट दोनों को अलग-अलग कैसे एक्सेस कर सकता हूं?

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") 

सबसे अच्छा काल्पनिक "समाधान" मैंने पाया अब तक शामिल हैं stdout, inspired by this डाइवर्ट, एक फ़ाइल धारा को और फिर उस फ़ाइल (से पढ़ने सुपर बदसूरत जा रहा है मैं पढ़ने में सक्षम नहीं किया गया इसके अलावा फ़ाइल से लिखने के बाद सीधे। क्या "कस्टम बफर स्ट्रीम" बनाना संभव है जिसे फ़ाइल में स्टोर करने की आवश्यकता नहीं है?)। हालांकि यह एक साइड ट्रैक की तरह 'थोड़ा' लगता है।

एक और कोण 'hGetContents stdout' का उपयोग करने लगता है अगर ऐसा लगता है कि मुझे ऐसा करना चाहिए। लेकिन मुझे stdout से पढ़ने की अनुमति नहीं दी गई है। हालांकि गुगलिंग यह दिखाता है कि यह used रहा है।

+0

आपके उपयोग के मामले के आधार पर, और पोर्टेबिलिटी एक मुद्दा है, तो आप इस प्रयोगात्मक पुस्तकालय का उपयोग कर सकते हैं: https://hackage.haskell.org/package/system-posix-redirect-1.1.0.1/docs/System-Posix -Redirect.html –

उत्तर

4

क्यों न केवल लेखक मोनड का उपयोग करें? उदाहरण के लिए,

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") 

वैकल्पिक रूप से, आप एक Handle लेने के लिए करने के बजाय stdout लिखने के लिए अपने भीतर कार्रवाई को संशोधित कर सकता है। इसके बाद आप knob जैसे कुछ मेमोरी फ़ाइल हैंडल बनाने के लिए उपयोग कर सकते हैं जिसे आप आंतरिक क्रिया में पारित कर सकते हैं और बाद में इसकी सामग्री जांच सकते हैं।

+0

बात यह है कि, मैं वास्तव में अपने वास्तविक कोड में io तर्क के बारे में बहुत कुछ नहीं कर सकता। इसके लिए बाहरी व्यवहार को फिर से डिजाइन करने की आवश्यकता होगी। "क्लाइंट एप्लिकेशन" को आईओ क्रियाएं (तर्क के रूप में) भेजना/पास करना होता है। तो किसी बिंदु या दूसरे पर मुझे 'कैचऑटपुट' जैसे कुछ उपयोग करना होगा। तो लेखक मोनड वास्तव में मेरी समस्या पर लागू नहीं है .. दूसरी तरफ घुंडी संभावित रूप से उपयोगी लगता है। :) – worldsayshi

+1

@worldsayshi आप हथौड़ा के सुझाव को गलत समझते हैं। ध्यान दें कि उन्होंने 'राइटर' मोनैड का उपयोग नहीं किया था, लेकिन 'राइटर टी आईओ' मोनैड - ताकि आप अभी भी 'लिफ्टियो आईओ' का उपयोग करके अपनी 'आईओ' कार्रवाई को कॉल कर सकें। –

+0

मैंने अपने कोड में सुधार किया, इसके बारे में खेद है। शायद उसमें कुछ भ्रम।जो मैं पहली बार ढूंढ रहा हूं वह स्टडआउट में समाप्त होने वाला पाठ है। मेरे उदाहरण में _now_ 'res' क्या है। मैं नहीं देखता कि लिफ्टियो मुझे उस तक पहुंच कैसे दे सकता है। फिर मैं चाहता हूं कि आउटपुट वास्तव में आईओ एक्शन भी चलाए। यह वर्तमान में 'कैचऑटपुट' से अनदेखा आउटपुट है। यदि मुझे एक 'आईओ()' एक तर्क उठाने के रूप में मिलता है तो यह मुझे 'एम()' देगा। – worldsayshi

1

रूप @hammar ने कहा, आप एक में स्मृति फ़ाइल बनाने के लिए एक घुंडी उपयोग कर सकते हैं, लेकिन आप यह भी फिर से वापस hDuplicate और hDuplicateTo उपयोग कर सकते हैं स्मृति फाइल करने के लिए stdout बदलने के लिए, और। निम्नलिखित पूरी तरह से अपरीक्षित कोड की तरह कुछ:

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

हम्म, अब मैंने वास्तव में 'डेटा.कनोब' स्थापित किया है, मुझे यह विधि काम करने के लिए नहीं मिल सकती है। 'HduplicateTo' पर कॉल निम्न त्रुटि के साथ 'stdout' पर knob हैंडल को डुप्लिकेट करने का प्रयास करते समय विफल रहता है:' Wots: : hDuplicateTo: अवैध ऑपरेशन (हैंडल असंगत हैं)'। – pat

3

मैं एक समारोह है कि stdout के लिए प्रिंट की एक इकाई परीक्षण के लिए निम्नलिखित समारोह का इस्तेमाल किया।

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 

मैं में स्मृति फ़ाइल दृष्टिकोण के बारे में निश्चित नहीं हूँ, लेकिन यह एक अस्थायी फ़ाइल के साथ उत्पादन की एक छोटी राशि के लिए ठीक काम करता है।

4

हैकेज पर कुछ पैकेज हैं जो ऐसा करने का वादा करते हैं: io-capture और चुपचाप। चुपचाप बनाए रखा जाता है और विंडोज़ पर भी काम करता है (आईओ-कैप्चर केवल यूनिक्स पर काम करता है)। चुपचाप साथ, आप पर कब्जा का उपयोग करें:

import System.IO.Silently 

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

ध्यान दें कि यह एक अस्थायी फ़ाइल के लिए उत्पादन पुनः निर्देशित कर काम करता है और फिर इसे पढ़ें ... लेकिन जब तक यह काम करता है!