[Haskell-cafe] Trouble with simple embedding of shell in haskell

Bulat Ziganshin bulat.ziganshin at gmail.com
Thu Sep 21 04:50:37 EDT 2006

Hello George,

Thursday, September 21, 2006, 7:44:22 AM, you wrote:

> sh :: String -> String -> IO String
> sh cmd = \input -> 
>          do (stdin, stdout, _, pid) <- runInteractiveCommand cmd
>             forkIO $ hPutStr stdin input >> hClose stdin
>             -- get exit status to prevent zombie accumulation
>             forkIO $ waitForProcess pid >> return ()
>             hGetContents stdout

1. when 'main' finishes, all the threads created by forkIO, silently
dies. typically you need to wait until they are finished. ghc
libraries don't provide any provision for it, they are too low-level.
you can consider writing your own simple wrapper using MVar:

myFork action = do
  v <- newEmptyMVar
  forIO $ action >>= putMVar v
  return v

finishHim v = takeMVar v

which also returns value returned by action. usage:

subProcess <- myFork $ do-something-here
finishHim subProcess

or you can use some simple libraries which use more developed
solutions of this problem:
http://freearc.narod.ru/Process.tar.gz (my own)

2. afaiu, you don't want to wait for process finishing before calling
getContents? in this case you need to collect all 'pids' returned by
myFork function in some list and finish them all before main returns.
or, better, develop more complex interface that will allow to kill
processes whose output is no more required, in order to help
'head'-like processes

also look at
and file i attached

also, look at "Tackling the awkward squad: monadic input/output,
concurrency, exceptions, and foreign-language calls in Haskell"
paper, which is a definitive source of information about GHC
concurrency implementation

Best regards,
 Bulat                            mailto:Bulat.Ziganshin at gmail.com
-------------- next part --------------
Following the "release early, release often" motto, I am happy to
announce version 0.1.0 of HSH, the Haskell shell.

You may obtain it with:

darcs get --tag 0.1.0 http://darcs.complete.org/hsh

Things are still very rough in many ways, but this version already lets

 * Run commands

 * Pipe things between commands

 * Pipe command input/output into and out of pure Haskell functions

 * Pure Haskell functions are as much a first-class citizen as
   is grep or cat

Here is an example session:

$ ghci -fglasgow-exts HSH

*HSH> run $ ("ls", ["."])
COPYING    HSH        HSH.hs    TODO    announcements  testsrc
COPYRIGHT  HSH.cabal  Makefile  _darcs  test.hs

*HSH> run $ ("ls", ["-l"]) -|- ("wc", ["-l"])

*HSH> :m +Text.Printf
*HSH Text.Printf> let countLines = (zipWith (\i line -> printf "%-5d %s" i line) [(1::Int)..])::([String] -> [String])

*HSH Text.Printf> run $ ("ls", ["-l"]) -|- countLines -|- ("grep", ["hs$"])
6     -rw-r--r-- 1 jgoerzen jgoerzen  1285 Jun  6 09:43 HSH.hs
11    -rw-r--r-- 1 jgoerzen jgoerzen   565 Jun  6 09:43 test.hs

*HSH Text.Printf> :m +Data.List
*HSH Text.Printf Data.List> run $ ("ls", ["-l"]) -|- countLines -|- filter (isSuffixOf "hs")
6     -rw-r--r-- 1 jgoerzen jgoerzen  1285 Jun  6 09:43 HSH.hs
11    -rw-r--r-- 1 jgoerzen jgoerzen   565 Jun  6 09:43 test.hs

*HSH Text.Printf Data.List> run $ ("ls", ["-l"]) -|- countLines -|- filter (isSuffixOf "hs") -|- ("tr", ["a-z", "A-Z"])
6     -RW-R--R-- 1 JGOERZEN JGOERZEN  1285 JUN  6 09:43 HSH.HS
11    -RW-R--R-- 1 JGOERZEN JGOERZEN   565 JUN  6 09:43 TEST.HS

*HSH Text.Printf Data.List> let generator = \(_::String) -> unlines . map show $ [1..20]
*HSH Text.Printf Data.List> generator ""
*HSH Text.Printf Data.List> run $ generator -|- ("grep", ["1"])

Future versions will likely simplify syntax to make it easier to write scripts
and introduce a sh to hsh converter.  I also plan to add pure Haskell
tools for some common shell-ish things that one could do in Haskell.

-- John

More information about the Haskell-Cafe mailing list