Create your own Deno utils with pipelines support

Once you discover Unix pipelines, you’ll know they can be extremely powerful. Here’s just a simple example to list a folder and filter on anything with an M.
ls | grep M
Write once, maintain never
When you start writing your own shell scripts sometimes it feels a bit like: “Write once, maintain never.” Especially when you don’t write shell scripts occasionally. You’ll need to add a loads of comments for your future self, just so can you read your own code…
Example from: https://fortes.com/2019/bash-script-args-and-stdin/
# Copy command-line arguments over to new array
ARGS=( $@ )
# Read in from piped input, if present, and append to newly-created array
if [ ! -t 0 ]; then
readarray STDIN_ARGS < /dev/stdin
ARGS=( $@ ${STDIN_ARGS[@]} )
fi
# Single loop to process all arguments
for ARG in "${ARGS[@]}"; do
echo "$ARG"
done
Pipelines with Deno stdin

With Deno, you can now have a webby experience; which means no more centralised package manager. Just like the old web days; Import a dependency from an URL and you’re ready to go. Together with first class support for TypeScript you’ll get a really nice experience out of the box.
Here is a minimal Deno example to pipe data to into a Deno script. This script will just prefix “Hello, ” and output the result.
// hello.tsimport { readLines } from "https://deno.land/std/io/bufio.ts";// Detect piped input
if (!Deno.isatty(Deno.stdin.rid)) {
for await (const line of readLines(Deno.stdin)) {
line && console.log(`Hello, ${line}`);
}
}
To run this script:
echo "Deno" | deno run hello.ts
And this will output “Hello, Deno”
You can also use an file as input; Let say you have a text file names.txt
Deno
TypeScript
When you run deno run hello.ts < names.txt.
It will output “Hello, Deno” and “Hello, TypeScript”
Merge pipe and arguments
Inspired by: https://fortes.com/2019/bash-script-args-and-stdin/
The Deno example below is a port of the bash script to merge pipe and arguments. It allows your script to be used with pipes or use arguments to provide input data.
import { readLines } from "https://deno.land/std/io/bufio.ts";const list: string[] = [];// Detect piped input
if (!Deno.isatty(Deno.stdin.rid)) {
for await (const line of readLines(Deno.stdin)) {
line && list.push(line);
}
}// merge pipe and args
[...list, ...Deno.args].forEach((item) => {
console.log(`Hello, ${item}`);
})
Your script now also accepts arguments as input:
deno run hello.ts "Deno"
For more control on arguments see: https://deno.land/std/flags/README.md
Webby Deno
Because of Deno’s webby nature. You can just put your script anywhere on the web and run execute it directly from there.
echo "Deno" | deno run https://gist.githubusercontent.com/chimurai/0c4327f708e60528d407f286730356b8/raw/225a7977c769507cdd761bb1a7f830fdbe04f2ed/hello.ts
If you want to “shout” and UPPERCASE the input. Just pipe it through another script:
echo "Deno" | deno run https://gist.githubusercontent.com/chimurai/0c4327f708e60528d407f286730356b8/raw/225a7977c769507cdd761bb1a7f830fdbe04f2ed/hello.ts | deno run https://gist.githubusercontent.com/chimurai/0c4327f708e60528d407f286730356b8/raw/fdca52a4912b08ddb9d8b7e0278040d7058a1fa4/shout.ts
Deno executables
If you don’t want to deal with the long urls; Install the Deno script as executable.
Install executable
Install the scripts and optionally rename the executable with the--name
flag:
deno install --name hello https://gist.githubusercontent.com/chimurai/0c4327f708e60528d407f286730356b8/raw/225a7977c769507cdd761bb1a7f830fdbe04f2ed/hello.tsdeno install --name shout https://gist.githubusercontent.com/chimurai/0c4327f708e60528d407f286730356b8/raw/225a7977c769507cdd761bb1a7f830fdbe04f2ed/shout.ts
Configure $PATH
Scripts are installed in this folder: ~/.deno/bin
Add ~/.deno/bin to PATH (add it to your~/.bashrc
or ~/.zshrc
)
export PATH="$HOME/.deno/bin:$PATH"
Reload your terminal with source ~/.bashrc
or source ~/.zshrc
or start a fresh one.
Give it a try
You should be able to run this:
echo "Deno" | hello | shout
And it will output: HELLO, DENO!
After thoughts
Deno’s webbyness and first class TypeScript support is awesome! This vanilla experience will instantly make you feel like home.
Rediscovering Unix Pipelines. As functional programming and RxJS usage is quite common in JavaScript and TypeScript development, you can reuse the same paradigm to author tooling for your shell environment. The scripts will be quite simple and composable. Hopefully this will prevent the tendency for scripts to become a monolithic nightmare.
As the Deno ecosystem grows I can only imagine what the possibilities will be.
Now, which one do you prefer?
Before with: Bash
# Copy command-line arguments over to new array
ARGS=( $@ )
# Read in from piped input, if present, and append to newly-created array
if [ ! -t 0 ]; then
readarray STDIN_ARGS < /dev/stdin
ARGS=( $@ ${STDIN_ARGS[@]} )
fi
# Single loop to process all arguments
for ARG in "${ARGS[@]}"; do
echo "$ARG"
done
After with: TypeScript
import { readLines } from "https://deno.land/std/io/bufio.ts";const list: string[] = [];// Detect piped input
if (!Deno.isatty(Deno.stdin.rid)) {
for await (const line of readLines(Deno.stdin)) {
line && list.push(line);
}
}// merge pipe and args
[...list, ...Deno.args].forEach((item) => {
console.log(`${item}`);
})