File: README.md

package info (click to toggle)
haskell-shell-conduit 5.0.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 112 kB
  • sloc: haskell: 607; makefile: 3
file content (194 lines) | stat: -rw-r--r-- 4,585 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
shell-conduit [![Hackage](https://img.shields.io/hackage/v/shell-conduit.svg?style=flat)](https://hackage.haskell.org/package/shell-conduit) [![Build Status](https://travis-ci.org/psibi/shell-conduit.svg?branch=master)](https://travis-ci.org/psibi/shell-conduit)
=====

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

[Haddock API documentation](https://www.stackage.org/package/shell-conduit).

### Examples

##### Cloning and initializing a repo

``` haskell
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:

``` haskell
λ> 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:

``` haskell
λ> 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:

``` haskell
λ> 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.

``` haskell
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:

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

Or shell commands:

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

Or conduits:

``` haskell
λ> run (cat $| conduit (awaitForever yield))
hello
hello
Interrupted.
```

##### Keyboard configuration

``` haskell
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:

``` haskell
rmdir :: ProcessType r => r
ls :: ProcessType r => r
```

But ultimately the types end up being:

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

Etc.

Run all shell scripts with

``` haskell
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.

``` haskell
{-# 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:

``` haskell
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](http://hackage.haskell.org/package/conduit-extra).

Also of interest is
[csv-conduit](http://hackage.haskell.org/package/csv-conduit),
[html-conduit](http://hackage.haskell.org/package/html-conduit), and
[http-conduit](http://hackage.haskell.org/package/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.