Shell scripting with Haskell Franz Thoma Berlin, 2017-02-24 - - PowerPoint PPT Presentation

shell scripting with haskell
SMART_READER_LITE
LIVE PREVIEW

Shell scripting with Haskell Franz Thoma Berlin, 2017-02-24 - - PowerPoint PPT Presentation

Shell scripting with Haskell Franz Thoma Berlin, 2017-02-24 Overview Shell scripting with high-level languages The turtle library Scripts & Dependency Management Parsing command line options A small application Conclusion Shell


slide-1
SLIDE 1

Shell scripting with Haskell

Berlin, 2017-02-24 Franz Thoma

slide-2
SLIDE 2

Overview

Shell scripting with high-level languages The turtle library Scripts & Dependency Management Parsing command line options A small application Conclusion

slide-3
SLIDE 3

Shell scripting with high-level languages

slide-4
SLIDE 4

Why use a high-level language for scripting

Abstraction: Support for data structures, types and encapsulation helps allow cleaner semantics. Flexibility: High-level languages provide a rich set of both high-level and low-level libraries. Scalability: Module systems keep growing applications

  • rganized.

Robustness: All of these make refactoring easier and applications more resilient.

slide-5
SLIDE 5

Why use a statically typed language for scripting

Dynamically typed languages are pretty popular in the scripting world as they are easy to hack away with. However, they share a number of problems with bare shell scripts: As scripts grow larger, the initial flexibility now makes the application increasingly harder to reason about. Statically typed programs are easy to refactor and extend

slide-6
SLIDE 6

Why use Haskell for scripting

Concise syntax, virtually no boilerplate Good library support, e.g. command line option parsers, ncurses bindings Can be interpreted using runhaskell or stack runhaskell

slide-7
SLIDE 7

The turtle library

slide-8
SLIDE 8

The turtle library

turtle is an implementation of the UNIX command line environment in Haskell. The idea is to provide a set of recognizeable functions for accessing the file system, streaming data, and job control.

slide-9
SLIDE 9

Demo

:set -XOverloadedStrings import Turtle import qualified Data.Text as Text import qualified Filesystem.Path.CurrentOS as Path projectDir <- pwd print projectDir cd =<< home pwd cd projectDir pwd view (ls ".") let vi file = proc "vi" [file] empty vi "README.md" vi ".ghci"

slide-10
SLIDE 10

Shell commands and their types

Turtle exposes some default shell commands: echo :: Line -> IO () cd :: FilePath -> IO () mv :: FilePath -> FilePath -> IO () cp :: FilePath -> FilePath -> IO () rm :: FilePath -> IO () pwd :: IO FilePath

slide-11
SLIDE 11

Building your own commands

The proc function allows calling external commands: Example:

proc :: Text -- Command

  • > [Text] -- Arguments
  • > Shell Line -- Lines of standard input
  • > IO ExitCode

vi :: FilePath -> IO ExitCode vi file = proc "vi" [format fp file] empty

slide-12
SLIDE 12

Shell streams

What about piping standard output to less?

less :: Shell Line -> IO ExitCode less txt = proc "less" [] txt

slide-13
SLIDE 13

The Shell type

Shell a is a stream of items of type a, with the possibility to execute IO actions. stdin :: Shell Line input :: FilePath -> Shell Line yes :: Shell Line select :: [a] -> Shell a ls :: FilePath -> Shell FilePath cat :: [Shell a] -> Shell a view :: Show a => Shell a -> IO ()

slide-14
SLIDE 14

Shell composition

Function application/composition can be used to compose shell actions: (.) and ($) act like unix pipes (but backwards): The bind operator (>>=) is the equivalent to shell expansions and xargs:

less' :: FilePath -> IO ExitCode less' = less . input

  • - »cat <file> | less«

dircat :: FilePath -> Shell Line dircat dir = ls dir >>= input

  • - »cat $(ls <dir>)«
  • - or »ls <dir> | xargs cat«
slide-15
SLIDE 15

Scripts & Dependency Management

slide-16
SLIDE 16

runhaskell

GHC has a script interpreter that can be used in a shebang line: However, this fails unless turtle is installed globally in the user environment.

#!/usr/bin/env runhaskell {-# LANGUAGE OverloadedStrings #-} import Turtle main = echo "Hello, World"

slide-17
SLIDE 17

stack runhaskell

Stack has a remedy for the dependency problem:

#!/usr/bin/env stack

  • - stack runhaskell --resolver=lts-8.0 --package=turtle

{-# LANGUAGE OverloadedStrings #-} import Turtle main = echo "Hello, World"

slide-18
SLIDE 18

Parsing command line options

slide-19
SLIDE 19

Auto-generated CLIs

Turtle can generate this CLI for us:

> optparse/my-application.hs --help My Application Usage: my-application.hs Available options:

  • h,--help Show this help text

{-# LANGUAGE OverloadedStrings #-} import Turtle main = do command <- options "My Application" (pure ()) print command

slide-20
SLIDE 20

Parameters and options

turtle provides an API for parsing parameters and options:

data Options = Options { foo :: Bool , bar :: Maybe Text , baz :: Text } deriving (Show)

  • ptionsParser :: Parser Options
  • ptionsParser = liftA3 Options

(switch "foo" 'f' "To foo or not to foo") (optional (optText "bar" 'b' "A bar option")) (argText "BAZ" "Some baz args") > optparse/my-application-turtle.hs --help Parse some options Usage: my-application-turtle.hs [-f|--foo] [-b|--bar BAR] BAZ Available options:

  • h,--help Show this help text
  • f,--foo To foo or not to foo
  • b,--bar BAR A bar option

BAZ Some baz args

slide-21
SLIDE 21

Simple CLIs

Sometimes only one or two simple parameters need to be

  • passed. The

library requires even less boilerplate to generate a CLI.

  • ptparse-generic

{-# LANGUAGE DeriveGeneric, OverloadedStrings #-} import Options.Generic data Positional = Positional Text Int (Maybe Text) deriving (Show, Generic) instance ParseRecord Positional main = do command <- getRecord "My Application" :: IO Positional print command > optparse/my-application-positional.hs --help My Application Usage: my-application-positional.hs TEXT INT [TEXT] Available options:

  • h,--help Show this help text
slide-22
SLIDE 22

bash auto-completion

... is provided out of the box:

source <( my-application --bash-completion-script $(which my-application) )

slide-23
SLIDE 23

A small application

slide-24
SLIDE 24

Demo

brick/select-file.hs

slide-25
SLIDE 25

Conclusion

slide-26
SLIDE 26

Conclusion

Haskell has a rich ecosystem for scripting and small CLI applications: turtle for shell-like file-system access, external processes, and streaming

  • ptparse-applicative for declarative command line
  • ption parsing

brick (and vty) as a lightweight ncurses textual interface stack with stack runhaskell for ad-hoc dependency management

slide-27
SLIDE 27

Thank you

Demos and slides on Github: github.com/fmthoma/shell-scripting-with-haskell