# Shh [![](https://img.shields.io/hackage/v/shh.svg?colorB=%23999&label=shh)](http://hackage.haskell.org/package/shh) [![](https://img.shields.io/hackage/v/shh-extras.svg?colorB=%23999&label=shh-extras)](http://hackage.haskell.org/package/shh-extras) [![](https://builds.sr.ht/~lukec/shh/nix.yml.svg)](https://builds.sr.ht/~lukec/shh/nix.yml?) Shh is a library to enable convinient shell-like programming in Haskell. It works well in scripts, and from GHCi, allowing you to use GHCi as a shell. It's primary purpose is in replacing shell scripts. As such, many functions are provided to mimic the shell environment, and porting shell scripts to shh should be fairly straightforward. A simple ["cargo culting" port](docs/porting.md) should work in most situations, and perhaps be even more robust than the original. It is also a wrapper tool around launching GHCi as a shell. It supports * Automatically defining a function for each executable on your `$PATH` using template Haskell, as well as a runtime check to ensure they all exist on startup. * Redirction of stdout and stderr -- Redirect stdout λ echo "Hello" &> StdErr λ echo "Hello" &> Truncate ".tmp_file" -- Redirect stderr λ echo "Hello" &!> Append "/dev/null" λ echo "Hello" &!> StdOut * Piping stdout or stderr to the input of a chained process λ cat "/dev/urandom" |> xxd |> head "-n" 5 * Multiple processes sequentially feeding a single process λ (echo 1 >> echo 2) |> cat * Use of Haskells concurrency primitives. λ race (sleep 1) $ curl "http://this_needs_to_timeout_after_1_second" λ d <- readTrim $ mktemp "-d" λ :{ | System.Directory.withCurrentDirectory d $ do | mapConcurrently_ (curl "-LOJs") | [ "https://raw.githubusercontent.com/luke-clifton/shh/master/shell.nix" | , "https://raw.githubusercontent.com/luke-clifton/shh/master/shh.cabal" | ] | ls | :} * Capturing of process output λ s <- echo "Hello" |> tr "-d" "l" |> capture λ loggedIn <- nub . words <$> readProc users λ putStrLn $ "Logged in users: " ++ show loggedIn λ mapM_ putStrLn =<< readSplit0 (Shh.Example.find "-maxdepth" 1 "-print0") * Capturing infinite output of a process lazily λ withRead (cat "/dev/urandom" |> xxd) $ mapM_ putStrLn . take 3 . lines 00000000: 8fcb ebee 9228 a897 3bfc 1d05 491d aceb .....(..;...I... 00000010: 47de 3ea3 2788 44ac 9b85 0a0f a458 b949 G.>.'.D......X.I 00000020: 5308 ddfe 5790 5a5f 39e3 bbb6 b689 2b03 S...W.Z_9.....+. * Write strings to stdin of a process. λ writeProc cat "Hello\n" Hello λ "Hello" >>> shasum f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 - λ shasum <<< "Hello" f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 - * Proper exceptions, when a process exits with a failure code, an exception is thrown. You can catch these normally. The exception includes the error code, the command, and all it's arguments. λ false "Ha, it died" *** Exception: Command `false "Ha, it died"` failed [exit 1] λ catchCode false 1 * "Native" processes, i.e. Haskell functions that behave like a process. λ echo "Hello" |> pureProc (map toUpper) |> tr "-d" "L" HEO * And much, much more! Look at the documentation on Hackage for a comprehensive overview of all the possibilities. ## Mnemonics Shh has many symbols that might seem intimidating at first, but there is a simple mnemonic for them. | Piping. Looks like a pipe, same as in POSIX shells. & Redirection, think of the shell `2>&1` >,< The direction of flow of a command ! Operate on stderr instead of stdout So, for example, ls |> cat Pipe the stdout of `ls` into stdin of `cat` cat <| ls Same as above ls &> StdErr Redirect stdout of `ls` to wherever stderr is going. StdErr <& ls Same as above ls &!> StdOut Redirect stderr of `ls` to wherever stdout is going. StdOut