hrbrmstr's Daily Drop

Share this post

Drop #216 (2023-03-09): EPA (Environment Protection & Agency)

dailyfinds.hrbrmstr.dev

Drop #216 (2023-03-09): EPA (Environment Protection & Agency)

declare/readonly; direnv/quickenv; autoenv

boB Rudis
Mar 9
Share this post

Drop #216 (2023-03-09): EPA (Environment Protection & Agency)

dailyfinds.hrbrmstr.dev

Since we encountered some terminal velocity in yesterday's Drop, we'll join Master Splinter's 🐢 crew for the day and stay in our (Linux/macOS/BSD) shells.

Today's Drop looks at some ways to make our CLI environment variable ops safer and how to wield them with the efficiency of a 🥷🏼 with some new and new-ish helper tools.

typeset/readonly

Just under a year ago, we covered ShellCheck, a linter for your shell scripts. ShellCheck helps us write defensively, making our scripts more resistant to bugs/info leaks, and less likely to have exploitable weaknesses.

Now, the typeset (a.k.a. declare) and readonly (alternatively typeset -r) shell commands are not in any way, shape, or form novel entities. However, the other sections in today's Drop are focused on some new-ish ways to better manage environment variables, and it never hurts to toss in a PSA every now and again.

If you already know about these commands, feel free to skip to the remainder of the content, and just consider this your, now persistent, conscience nudge to write safer scripts 😎.

For those that have stuck around, I 100% know you would rather not read a 5,000-word essay on two simple shell builtins, so we'll make this quick. Plus, we're assuming bash and ksh are in play, and there are enough not-so-subtle differences in the typeset command between them:

  • bash: typeset [-afFgrxilnrtux] [-p] [name[=value] …]

  • ksh: typeset [ -CDHLRZfilrtux [n]] [name[=value]] …

that digging into all the functionality of typeset is left as an exercise to the reader (we'll just be covering a couple concepts, below).

Now, I know you know how to make an environment variable:

BEST_SHIP="rocinante"

And, I know that you even know how to make that variable available to the environment of subsequently executed commands:

export BEST_SHIP="rocinanate"

And, I trust that you always follow good quoting practices, right?

👍🏽

Because we “live”/work in the shell REPL, it's often easy to forget that we're using a full-on programming language. Even when we write scripts semi-regularly, I bet a decent percentage of our collective noggins lean into treating them like just lists of commands to execute, with the occasional conditional/loop/variable tossed in for good measure. But, they truly are real programming languages, and we can and should treat their variable ops with the same care and attention we would in any other programming language.

The readonly command does what it says on the tin — prevents accidental (or malicious) value modifications elsewhere in a script or other scripts. This will improve overall script stability, predictability, and security. Typing eight extra characters (nine, if you include a space) is a small price to pay, and the benefits far outweigh any increase in script file size. Plus, tagging variables as readonly also helps communicate intent to your future self and anyone else who needs to read or modify the script.

As far as typeset goes, the core item I wanted to convey in this section is that using it helps enforce and communicate the expected type of a given variable. We'll work through one, short example, since I suspect you're eager to get to the other sections and are quite capable of digging into your local help for typeset.

There are originally four members of the crew of the Rocinante, so we'll make that integer (i) fact known in a script we're writing, and make sure it's available (x) by anything we call, and restrict modification (r) of the value:

$ typeset -xri ROCI_CREW_COUNT=1

Folks who use ksh have a nice way of validating that:

$ echo ${(t)ROCI_CREW_COUNT}
integer-readonly-export

If you haven't poked at these typeset / declare / readonly (and the other shell helpers to make working with variables safer), consider this license to do so.

Share

direnv/quickenv

red blue and yellow ceramic figurine
Photo by Didssph on Unsplash

Back in February, we lightly touched on “run commands” when talked about dotfile management. These files usually end in rc, and that idiom has carried over into other contexts.

One such context is the direnv project, which aims to help you manage your environment variables in a slightly similar fashion as you may use .gitignore files.

Git “ignore” files let you specify files and directories that should be excluded from git ops. You can put one in nested directories to customize how those ignore rules are applied.

Direnv — which is a single Golang binary — takes that concept and applies to your environment variables. Once installed, it becomes an extension for your shell. By that, I mean it hooks pretty deep into your shell, such that — when you cd around the place — loads and unloads environment variables depending on the current directory. This is handy if you need different environment variables for different projects, and helps declutter your environment.

It does so by looking for local .envrc files.

Four popular shells are supported:

  • bash

  • zsh

  • tcsh

  • fish

And the documentation {GH} is solid enough that I can leave you in their hands.

If you're looking for something a bit less intrusive, quickenv provides similar functionality, but requires a bit more work on your part.

autoenv

gray vehicle gear shift lever
Photo by Markus Spiske on Unsplash

Autoenv is a Rust utility that works pretty much like the aforementioned direnv with a few differences:

  • it uses .env vs .envrc

  • has a “view-before-execute” mode

  • if the option is enabled and an .env.leave file is present in a directory, it will get executed when you cd out

  • you can control which .env[.leave] files are authorized (to prevent someone from injecting code by slipping in an unexpected .env[.leave] file into random directories)

  • supports dash (and does not support tcsh)

FIN

There are many limitations with the “legacy” shells we mentioned today. Just like other programming languages, creative minds continue to adopt, adapt, and improve upon the concept of an operating system shell, and you may want to check out one or more alternative ones to level up your shell game. ☮

#declare #readonly #shell #bash #ksh #env #printenv #cli #repl #autoenv #quickenv #direnv #dash

Share this post

Drop #216 (2023-03-09): EPA (Environment Protection & Agency)

dailyfinds.hrbrmstr.dev
Previous
Next
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 boB Rudis
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing