Shebangs
dt is shebang-friendly. When you have something that gets too long for a one-liner, or you have something you want to save and not type out a lot, you can put that into a shebang file.
Note: It's been more than 20 years since I've been familiar with Windows. I think the equivalent in a Windows environment these days would be writing a PowerShell Script Module with a
.psm1
extension. If we get Windows support and anyone knows a good pattern let us know!
An introduction
If you're familiar with Unix-like systems, you can skip this section.
The "Hash Bang" -- #!
-- more often called "shebang" is a magic prefix for
executable` files that tell operating systems like Linux, BSD, or OSX to use
a specific interpreter to interpret the file.
More specifically, let's write a file named greet
with contents like:
#!/usr/bin/env dt
"Hello world!" println
Mark it executable (chmod +x ./greet
) and you should be ready to run it:
$ ./greet
Hello world!
If you've been following along in the past sections, you've now greeted the world at least 3 times. Hope the world has noticed you by now!
The #!
says the rest of the line is something to execute as a process.
/usr/bin/env dt
helps locate a program called dt
and execute it with the
remaining arguments. dt
in turn will get the filename as an argument and
start to interpret the file contents.
If you had installed dt
in a place like /home/me/.local/bin/dt
you could
also do something like:
#!/home/me/.local/bin/dt
"Hello world!" println
Shared scripts tend to use the /usr/bin/env
lookup just in case the install
location can't be known. I don't know about you, but I tend to share scripts,
even if it's just with my future self! Who knows what that guy will do.
Anyway the point here is that you can put dt code in a file, and that can be an executable.
Shebang scripting with dt
Let's start our shebang examples by making a couple naive implementations of other common tools. (Of course: Do use the tools instead of these scripts!) We'll skip a lot of the niceties like usage messages and flag parsing, and just implement the core use case.
Let's create a head.dt N
that can print the first N lines of its input. Put
the following in a file called head.dt
and mark it as executable.
#!/usr/bin/env dt
shebang-args first \n:
[readln println] n times
We use shebang-args
to get the arguments passed to the shebang file. This is
just the args of dt minus the first arg which is the script being interpreted.
We bound the first argument to n
and then did a read/print loop n
times.
It can be used similar to the classic head -n N
pattern:
$ seq 100 | head.dt 5
1
2
3
4
5
Now let's create a scream.dt
that can be an equivalent of tr a-z A-Z
.
#!/usr/bin/env dt
[readln upcase println] loop
$ echo -e "i\ncan't\nhear\nyou" | scream.dt
I
CAN'T
HEAR
YOU
Ok folks I get it, it's short, but we're here to demonstrate, not to
optimize! The big thing to know is you can loop
which is a "forever" kind
of iteration, and when the pipe completes, dt will exit gracefully.
Note: Very short things also work as shell aliases. Here's a way to do the same thing in POSIX conventions:
alias scream='dt stream [rl upcase pl] loop
Shebangs into REPLs
Another pattern that can be helpful is doing some pre-work like defining some commands or reading files and then dropping into a REPL with that state.
We won't go into a ton of detail here, but we'll demonstrate with a custom prompt and a single command definition.
#!/usr/bin/env dt
"MyShell 0.1" println
["/home/me/myshell.log" appendf]
"( s -- ) Save a string to the myshell log."
\log define
repl