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

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

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
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 Chris Done
Author Chris Done
Maintainer chrisdone@gmail.com
Revised Revision 1 made by AdamBergmark at 2016-05-25T14:48:25Z
Category Conduit, Scripting
Home page https://github.com/chrisdone/shell-conduit
Source repo head: git clone https://github.com/chrisdone/shell-conduit.git
Uploaded by ChrisDone at 2015-02-13T08:23:34Z
Distributions Debian:4.7.0, LTSHaskell:5.0.0, NixOS:5.0.0
Reverse Dependencies 3 direct, 0 indirect [details]
Downloads 14611 total (51 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2015-02-13 [all 1 reports]

Readme for shell-conduit-4.5.1

[back to package description]

shell-conduit Hackage

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.