shell-conduit: Write shell scripts with Conduit

[ bsd3, conduit, library, scripting ] [ Propose Tags ]

Write shell scripts with Conduit. See Data.Conduit.Shell for documentation.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.0, 0.1, 1.0, 1.1, 2.0, 3.0, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.5.1, 4.5.2, 4.6.0, 4.6.1, 4.7.0, 5.0.0
Change log CHANGELOG.md
Dependencies async (>=2.0.1.5), base (>=4 && <5), bytestring, conduit, conduit-extra, control-monad-loop, directory, filepath, monad-control, monads-tf, process (>=1.2.1.0), resourcet, semigroups, split, template-haskell, text, transformers, transformers-base, unix (>=2.7.0.1) [details]
License BSD-3-Clause
Copyright 2014-2017 Chris Done
Author Chris Done
Maintainer Sibi Prabakaran <sibi@psibi.in>
Category Conduit, Scripting
Home page https://github.com/psibi/shell-conduit
Bug tracker https://github.com/psibi/shell-conduit/issues
Source repo head: git clone http://github.com/psibi/shell-conduit
Uploaded by psibi at 2017-03-06T20:34:27Z
Distributions Debian:4.7.0, LTSHaskell:5.0.0, NixOS:5.0.0
Reverse Dependencies 3 direct, 0 indirect [details]
Downloads 14588 total (47 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2017-03-06 [all 1 reports]

Readme for shell-conduit-4.6.1

[back to package description]

shell-conduit Hackage Build Status

Write shell scripts with Conduit. Still in the experimental phase.

Haddock API documentation.

Examples

Cloning and initializing a repo
import Control.Monad.IO.Class
import Data.Conduit.Shell
import System.Directory

main =
  run (do exists <- liftIO (doesDirectoryExist "fpco")
          if exists
             then rm "fpco/.hsenvs" "-rf"
             else git "clone" "git@github.com:fpco/fpco.git"
          liftIO (setCurrentDirectory "fpco")
          shell "./dev-scripts/update-repo.sh"
          shell "./dev-scripts/build-all.sh"
          alertDone)
Piping

Piping of processes and normal conduits is possible:

λ> run (ls $| grep ".*" $| shell "cat" $| conduit (CL.map (S8.map toUpper)))
DIST
EXAMPLES
LICENSE
README.MD
SETUP.HS
SHELL-CONDUIT.CABAL
SRC
TAGS
TODO.ORG
Running actions in sequence and piping

Results are outputted to stdout unless piped into other processes:

λ> run (do shell "echo sup"; shell "echo hi")
sup
hi
λ> run (do shell "echo sup" $| sed "s/u/a/"; shell "echo hi")
sap
hi
Streaming

Live streaming between pipes like in normal shell scripting is possible:

λ> run (do tail' "/tmp/example.txt" "-f" $| grep "--line-buffered" "Hello")
Hello, world!
Oh, hello!

(Remember that grep needs --line-buffered if it is to output things line-by-line).

Handling exit failures

Process errors can be ignored by using the Alternative instance.

import Control.Applicative
import Control.Monad.Fix
import Data.Conduit.Shell

main =
  run (do ls
          echo "Restarting server ... ?"
          killall name "-q" <|> return ()
          fix (\loop ->
                 do echo "Waiting for it to terminate ..."
                    sleep "1"
                    (ps "-C" name >> loop) <|> return ())
          shell "dist/build/ircbrowse/ircbrowse ircbrowse.conf")
  where name = "ircbrowse"
Running custom things

You can run processes directly:

λ> run (proc "ls" [])
dist	  LICENSE    Setup.hs		  src	TODO.org
examples  README.md  shell-conduit.cabal  TAGS

Or shell commands:

λ> run (shell "ls")
dist	  LICENSE    Setup.hs		  src	TODO.org
examples  README.md  shell-conduit.cabal  TAGS

Or conduits:

λ> run (cat $| conduit (awaitForever yield))
hello
hello
Interrupted.
Keyboard configuration
import Data.Conduit.Shell
main =
  run (do xmodmap ".xmodmap"
          xset "r" "rate" "150" "50")

How it works

All executable names in the PATH at compile-time are brought into scope as runnable process conduits e.g. ls or grep.

All processes are bound as variadic process calling functions, like this:

rmdir :: ProcessType r => r
ls :: ProcessType r => r

But ultimately the types end up being:

rmdir "foo" :: Segment r
ls :: Segment r
ls "." :: Segment r

Etc.

Run all shell scripts with

run :: Segment r -> IO r

The Segment type has a handy Alternative instance.

String types

If using OverloadedStrings so that you can use Text for arguments, then also enable ExtendedDefaultRules, otherwise you'll get ambiguous type errors.

{-# LANGUAGE ExtendedDefaultRules #-}

But this isn't necessary if you don't need to use Text yet. Strings literals will be interpreted as String. Though you can pass a value of type Text or any instance of CmdArg without needing conversions.

Other modules

You might want to import the regular Conduit modules qualified, too:

import qualified Data.Conduit.List as CL

Which contains handy functions for working on streams in a list-like way. See the rest of the handy modules for Conduit in conduit-extra.

Also of interest is csv-conduit, html-conduit, and http-conduit.

Finally, see the Conduit category on Hackage for other useful libraries: http://hackage.haskell.org/packages/#cat:Conduit

All of these general purpose Conduits can be used in shell scripting.