Skip to content
/ shrun Public

A utility program for running shell commands concurrently.

License

Notifications You must be signed in to change notification settings

tbidne/shrun

Repository files navigation

Shrun

Run Shell Commands Concurrently

GitHub release (latest SemVer) ci MIT

linux apple

demo


Table of Contents

Motivation

shrun was borne of frustration. Suppose we need to run several shell commands on a regular basis e.g. updates after pulling the latest code. These can be run manually in separate terminals:

cmd1
cmd2
cmd3
...

But this can be a lot of repetitive typing, especially when the commands are longer. Thus an alias is born:

alias run_commands="cmd1 && cmd2 && cmd3 ..."

All well and good, but this approach has several deficiencies:

  1. There is no information about how long the commands have been running. If any of the commands are long-lived, how would we know when it has been "too long" and the commands should be cancelled? We could use a wall clock or a stopwatch, but that is imprecise and requires remembering every time the commands are run, which is certainly unsatisfying.

  2. These commands are all run synchronously even though there may be no relation between them. For example, three commands that each take 5 minutes will combine to take 15 minutes. This is usually unnecessary.

  3. Related to above, if any command fails then subsequent ones will not be run. This can be frustrating, especially when a quicker command in the beginning prevents a longer one at the end from even starting.

  4. If the alias is tweaked to run all regardless (cmd1; cmd2; cmd3 ...), then it can be difficult to determine which, if any, failed. Additionally, understanding logs is much harder.

  5. It does not scale. Imagine we have variations of cmd3 we want to run under different circumstances. We could create multiple aliases:

     alias run_commands_cmd3a="cmd1 && cmd2 && cmd3a"
     alias run_commands_cmd3b="cmd1 && cmd2 && cmd3b"
    

    But this is messy and grows exponentially in the number of aliases for each variation.

shrun purports to overcome these limitations.

Introduction

In a nut-shell (😉), shrun is a wrapper around running shell commands. For instance:

shrun "some long command" "another command"

Will run some long command and another command concurrently.

A running timer is provided, and stdout will be updated when a command finishes or crashes.

Installation

The releases page has binaries built for several platforms. If there are no binaries for your platform, it is possible to build shrun yourself.

Building

If you have never built a haskell program before, Cabal is probably the best choice.

Cabal

Prerequisites

The easiest way to install these is generally ghcup.

The current "blessed" version is ghc-9.8.2.

Build Shrun

Once you have cabal and ghc, shrun can be built locally with cabal build or installed globally (e.g. ~/.local/bin/shrun) with cabal install.

For further reproducibility, optional freeze files can be used e.g.

cabal build --project-file cabal.ghc982.project

Note

Freeze files are provided for only select compilers.

Stack

Prerequisites

Like cabal and ghc, stack can be installed with ghcup.

Build Shrun

Once you have stack, shrun can be built with stack build or installed globally (i.e. ~/.local/bin/shrun) with stack install.

Nix

Prerequisites

Manually

Building with nix uses flakes. shrun can be built with nix build, which will compile and run the tests.

Nix expression

Because shrun is a flake, it can be built as part of a nix expression. For instance, if you want to add shrun to NixOS, your flake.nix should have:

# flake.nix
{
  inputs.shrun.url = "github:tbidne/shrun/main";
}

Then include this in the systemPackages:

# wherever your global packages are defined
{
  environment.systemPackages = [
    shrun.packages."${system}".default
  ];
}

Configuration

See configuration.md for the available options.

FAQ

See faq.md.