[Various updates John Goerzen **20071025113437] { hunk ./en/ch20-systems.xml 723 + + Better Piping + + Our previous example solved the basic need of letting us set up + shell-like pipes. There are some other features that it would be + nice to have though: + + Supporting more shell-like + syntax + Letting people pipe data into external programs or + regular Haskell functions, freely mixing and matching the + two + Returning the final output and exit code in a way + that Haskell programs can readily use + + + Fortunately, we already have most of the pieces to support this in + place. We need only add a few more instances of + CommandLike to support this, and a few more + functions similar to runIO. Here is a revised + example that implements all of these features: + + &RunProcess.hs:all; + + Here's what has changed: + + + A new CommandLike instance for + &String; that uses the shell to evaluate and invoke the string. + + New CommandLike instances for + String -> IO String and various other types + that are implemented in terms of this one. These process Haskell + functions as commands. + + A new RunResult typeclass that + defines a function run that returns + information about the command in many different ways. See the + comments in the source for more information. + runIO is now just an alias for one particular + RunResult instance. + + A few utility functions providing Haskell + implementations of familiar Unix shell commands. + + + + + Let's try out the new shell features. First, let's make sure that + the command we used in the previous example still works. Then, + let's try it using a more shell-like syntax. + + &rp.ghci:shell; + + That was a lot easier to type. Let's try substituting our native + Haskell implementation of grep and try out some + other new features as well: + + &rp.ghci:hask; + hunk ./en/ch20-systems.xml 784 + + Final Words on Pipes + + We have developed a sophisticated system here. We warned you earlier + that POSIX can be complex. One other thing we need to highlight: you + must always make sure to evaluate the &String; returned by these + functions before you attempt to evaluate the exit code of the child + process. The child process will often not exit until it can write all of + its data, and if you do this in the wrong order, your program will + hang. + + + In this chapter, we have developed, from the ground up, a simplified + version of HSH. If you wish to use these shell-like capabilities in + your own programs, we recommend HSH instead of the example developed + here due to optimizations present in HSH. HSH also comes with a + larger set of utility functions and more capabilities. Some of the + utility functions presented here, in fact, were copied verbatim from + HSH. + + hunk ./examples/ch20/RunProcess.hs 9 +import Control.Exception(evaluate) +import System.Posix.Directory +import System.Directory(setCurrentDirectory) hunk ./examples/ch20/RunProcess.hs 19 -import System.Environment +import System.Posix.Env(getEnv) hunk ./examples/ch20/RunProcess.hs 107 - Nothing -> "/bin/sh" - Just x -> x + Nothing -> "/bin/sh" + Just x -> x hunk ./examples/ch20/RunProcess.hs 140 - iofunc = reutrn . func + iofunc = return . func hunk ./examples/ch20/RunProcess.hs 148 - where linedfunc = func (line input) >>= (return . unlines) + where linedfunc = func (lines input) >>= (return . unlines) hunk ./examples/ch20/RunProcess.hs 273 - -{- | Execute a 'CommandLike'. -} hunk ./examples/ch20/RunProcess.hs 284 -echo :: String -> String -echo = id +echo :: String -> String -> String +echo inp _ = inp hunk ./examples/ch20/RunProcess.hs 289 -egrep pat = filter (ismatch regex) +grep pat = filter (ismatch regex) addfile ./examples/ch20/rp.ghci hunk ./examples/ch20/rp.ghci 1 +--# shell +:l RunProcess.hs +runIO $ ("ls", ["/etc"]) -|- ("grep", ["m.*ap"]) -|- ("tr", ["a-z", "A-Z"]) +runIO $ "ls /etc" -|- "grep 'm.*ap'" -|- "tr a-z A-Z" + +--# hask +runIO $ "ls /etc" -|- grep "m.*ap" -|- "tr a-z A-Z" +run $ "ls /etc" -|- grep "m.*ap" -|- "tr a-z A-Z" :: IO String +run $ "ls /etc" -|- grep "m.*ap" -|- "tr a-z A-Z" :: IO [String] +run $ "ls /nonexistant" :: IO String +run $ "ls /nonexistant" :: IO ProcessStatus +run $ "ls /nonexistant" :: IO Int +runIO $ echo "Line1\nHi, test\n" -|- "tr a-z A-Z" -|- sortLines }