# DSH - Easier Scripting in D

Written on 5 May, 2022, by Andrew Lalis.

You're probably used to writing your automation scripts in a language like Bash, Powershell, Ruby, or Python. With DSH, it can be just as easy to write those scripts in D. I've designed DSH as a combination of a standalone programming library, and a set of tools to facilitate faster development and deployment. This article will discuss some of the basic use cases and how DSH works to make it easier to accomplish them.

In case you're interested, you can check out DSH on code.dlang.org or on GitHub.

## Why write scripts in D?

While many people are used to scripting in languages that have been built over the years specifically for that use case, this introduces extra complexity into projects that are mainly built using some other language. In an ideal world, we'd use one single language to describe everything about our app, from the way it looks, to how we compile and distribute it. DSH is my attempt to make this a reality, at least for applications written in D. When developing D programs that require some auxiliary scripts, you don't have to make a cognitive jump to another language; just keep writing code in the environment you're most efficient in. This way, we reap the benefits of specializing in becoming an expert in a single language and its ecosystem.

If you're still not fully convinced, that's alright; read ahead and see if any of my examples can sway your opinion.

## Writing Your Script

There are two main ways to write scripts using DSH:

- Add a `dub.sdl` package descriptor to the top of your file, and an executable shebang. For example:

      					#!/usr/bin/env dub
      					/+ dub.sdl:
      						dependency "dsh" version="~>1.6.1"
      					+/
      					import dsh;

      					void main() {
      						print("Hello world!");
      					}

  Execute this script via `./my_script.d`
- Use the single-file `dshs.d` version of DSH, for compiled scripts which don't use the dub package manager:

      					import dsh;

      					void main() {
      						print("Hello world!");
      					}

  Executing these scripts involves compiling them first. `dmd my_script.d /usr/include/dshs.d && ./my_script`

Generally, the dynamic form is more useful for scripts you'd like to distribute to anyone, since they only require the user to have the D toolchain installed in order to run it. However, it is slower, since the script and all its dependencies must be compiled on-the-fly.

Alternatively, the static form is more useful for complex scripts that are best pre-compiled, or for when you don't want to use the dub package manager, just a D compiler.

Luckily for either of these solutions, DSH comes with a `dshutil` tool to help you get started. You can install it like so:

    				wget https://raw.githubusercontent.com/andrewlalis/dsh/main/tools/dshutil.d -O dshutil.d
    				chmod +x dshutil.d
    				./dshutil.d install

I won't go into all the features of dshutil here, since you can read the full documentation on GitHub, but in short, it provides shortcuts for creating boilerplate scripts, automatically compiling when changes are detected, and gives you access to the dshs.d single-file library for static scripts.

## Helper Functions

While dshutil is a handy tool on its own, the real benefit of using DSH is its library of helper functions which are designed specifically for scripting tasks.

### File Utilities

DSH offers quite a few helpful functions that reduce the verbosity of your IO operations. Below is a sampling of some of the most prominent functions.

    			import dsh;

    			void main() {
    				copyDir("fromdir", "todir");
    				string filename = findFile("dir", "config\\.json");
    				string[] files = findFiles("dir", "^config.*$");
    				string[] dFiles = findFilesByExtension("dir", ".d");
    				string home = getHomeDir();
    				removeAnyIfExists(dFiles);
    			}

### Process Utilities

In addition to the functions in D's `std.process` module, DSH provides some shortcuts for scripts to make it easy to manage processes.

    			import dsh;

    			void main() {
    				run("cat out.txt");
    				runOrQuit("git pull"); // exits the program if the process fails.
    				setEnv("MY_VAR", "TRUE");
    				print(getEnv("MY_VAR"));
    			}

Also included is a `ProcessBuilder` class with which you can use a fluent method interface to construct processes with custom input and output sources and environments.

Back to Articles