Wandering Thoughts archives

2020-01-31

Finding out what directories exist with only basic shell builtins (a Unix shell trick)

Back in the old days of multi-architecture Unix environments, generally no two Unix vendors could agree on what should be in your $PATH. The core of /bin and /usr/bin was the same, but everyone had their own additional directories (Solaris had a lot of them). In addition, different local computing groups had different views on where local programs should go; /usr/local/bin, /local/bin, /opt/<something>/bin, /<group>/bin, and so on. This was a problem for me because I maintained a common set of dotfiles across all of the Unix systems I had accounts on, and I didn't want my $PATH to be a giant list of every possible directory on every system. So I needed to trim down a giant master list of possible $PATH directories to only the ones that existed on the current system, and to make it harder I wanted to use only shell builtins, in a shell where test wasn't a builtin.

Fortunately, there is one thing that has to be a builtin and has to fail if a directory doesn't exist (or isn't usable by you), namely cd. Using cd as a substitute for 'test -d' is a bit odd, but it works. My shell has real lists, so I could write this as more or less:

# potential $PATH entries in $candidates
path=`{ tpath=()
        for (pe in $candidates)
           builtin cd $pe >[1=] >[2=] && tpath=($tpath $pe)
        echo $tpath }

(This doesn't work for relative paths, but I didn't have any in my $PATH.)

Because all shells have to have cd as a builtin, this same trick could be used in pretty much any shell. Bourne style shells make you work a bit harder to put together $PATH; at a minimum you need to stick :'s between every element (cf), and maybe your equivalent of $candidates also uses :'s to separate entries and so you have to split it up on those.

(In a Bourne shell I would make $candidates just be quoted and space separated, because that's a lot simpler to deal with. This wouldn't handle a $PATH entry that had spaces in it, but you don't normally see those.)

Using cd this way is a trick, but tricks are what you're forced into in a minimal shell environment where you don't want to run external programs. In actual practice, I wound up writing a little C program to do this and relying on that on systems that I used frequently enough to compile it for them. The implementation with cd was only a fallback for systems and situations without my isdirs program.

(This is sort of a followup to a Unix shell trick, which was also about something that I had to do for cross-architecture dotfiles.)

PS: All of this comes from the days when systems were slow enough that you tried to avoid running additional external programs in your shell dotfiles, which is why I wanted to do it all with shell builtins instead of running a lot of tests or the like. And most modern shells have test as a builtin anyway.

unix/FilteringPATHWithBuiltins written at 23:14:53; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.