On my old Intel Macs, running older, pre-System Integrity Protection (SIP) versions of macOS and OS X, I’ve always stored my own custom shell scripts in /usr/local/bin. Specifically, scripts that I wanted to be able to run from Terminal, as any user on this particular Mac.
This was the Unix “best practices” location for storing such scripts, when I was working on Unix workstations and Linux boxes. When OS X came along, I checked around (e.g., the O’Reilly books), and they confirmed that /usr/local/bin was where I should be placing them.
Is this still the case for modern macOS versions? I seem to remember that when Monterey came along, there were warnings that, because of SIP, they should be stored somewhere else. And I notice Homebrew stores their apps under /opt.
Yes. Apple is pretty hands-off with respect to the entire /usr/local directory. I use it for everything I create that I want to be available for all users (and use ~/bin for things that are not for all users).
I’m sure /usr/local is not SIP-protected. If it was, I would have learned that the hard way a long time ago.
/opt is another long-time popular Unix location for third-party packages. Nothing wrong with that, if you prefer.
FWIW, here’s my manual-package-management system that I’ve been using on all my Unix-like systems (including Linux and macOS) for decades:
Install software packages to /usr/local/package/<packagename>/<version>.
For example, my installation of CMake version 3.21.3 is in /usr/local/package/cmake/3.21.3.
I specify the location as the “prefix” parameter when I run a package’s “configure” script, so it will install all of its files to the selected location.
When installing new versions, I add new version-specific directories, so the old one doesn’t go away until I want to explicitly delete it.
For each package, create a symlink called latest which points to the most recent version.
For example /usr/local/package/cmake/latest points to /usr/local/package/cmake/3.21.3
I then create symlinks from various locations in the package directory, via that latest symlink, to other locations under /usr/local.
And similar symlinks putting other important files in common locations, like /usr/local/lib, /usr/local/share/man/man1
The idea is that when I need to install a new version, I install it to a new version-directory under the same package directory. Then change the “latest” symlink to point to the new version number. Everything will typically just switch over.
If there’s a problem with the new version, I can change the “latest” symlink back. And when I’m confident that the new version is good, I can delete the old version’s directory.
Once upon a time, before Unix package managers became popular, I used this system for installing everything that didn’t come with the OS. Today, now that package managers are popular and robust, I only do this for software I build myself.
(I am aware of the fact that certain open source packages like Emacs and gcc have their own system for maintaining multiple installed versions. For these, I just install them to /usr/local/package/<packagename>, without a version, and let the package manage its own versions within that.)
Thanks very much for sharing this, David. I really like the idea of storing the scripts and their various support files and documents in /usr/local, and have symbolic links to the scripts in /usr/local/bin. I’m going to adapt this methodology on my new Mac.
I find it particularly handy, because I can include my rcs revision control repository in the same directory with the scripts.
I’ve found that it’s easier to use Git for local version control. Even without a remote server, I find that its tools are more robust and useful than the old RCS system.
Since today’s Macs usually are used as single user machines, rather than shared by many users, I think a case can be made that a user’s scripts are better kept in their own home directory, like ~/bin, but that’s getting a little pedantic. I’m sticking with /usr/local for compatibility with tradition and common practice.
No, Pedantic is reminding you that every machine should have a second account (usually adminstrator) for troubleshooting and recovery purposes - effectively making a multi-user machine where applications generally should not be installed under ~/.
I’m the only one who uses my Mac. But there are multiple accounts. At minimum an administrator account and a non-privileged account (my normal login). And I sometimes create new accounts in order to test/analyze problems.
In general, I want everything I install to be usable from all these accounts. Hence /usr/local/....
I completely agree — git is a far superior revision control system than RCS. I switched over from cvs to git around 2005, and once I got my head around it, used it for 95% of my software development and configuration work.
The only reason I stick with RCS for a handful of Unix shell configuration files and a couple of scripts is that I have about 30-40 years of revision history for those files in RCS.
If it was a pain to continue to use RCS, I’d switch over to git, and leave the RCS archives separately. But RCS is firmly in my mental firmware/muscle memory, so it’s not a hassle to use it for a few legacy files.
(It’s also possible that sentiment is involved; takes me back to look at scripting comments and log messages from the early days of my career. )
TLDR: Is there a book or online reference you’d recommend for this?
Back in the day when I was doing software development full time, I used several different source control systems, including Microsoft’s VSS, and CVS, but not git. VSS and CVS were easy to use (and I’m sure had their flaws). Now in my retirement, I’ve kind of gone back to those days, developing apps in Xcode. Git seems to be by far the king of the mountain now, and the basic commands are baked right into Xcode. But for the life of me, I don’t get git. How did you get your head around it? Is there a book, or online tutorial geared at explaining the zeitgeist of git? The command line interface is especially obscure to me.
I’ll +1 on the recommendations that the Git people have. In particular, I have used the ProGit book to wrap my head around Git. It’ll get you doing pretty quickly for the basics, and then you can use the rest of the book as you go.
And another vote here for the Git Pro book. In particular, the first 3 chapters – Getting Started, Git Basics and Git Branching – do the best job I’ve encountered to bringing one up to speed on git.
Back in the early 2010s, I was one of a small team within my software development group, who were tasked to bring the rest of the group up-to-speed on moving to git as our revision control system.
Our two starting points for team members learning git were the first 3 chapters of Pro Git, and an in-house wiki we wrote that had links to advanced topics, a few diagrams to help grok how git would be used in our development environment, and some “best practices” we’d come up with during our evaluation of git vs subversion (git won out handily) and initial prototyping of using git within our development system.
Management also splurged for a 1-week in-house class, which was a great deal of help; we had been using CVS for years, and getting our heads around git took some effort, because git had some fundamental use differences with CVS. The class wasn’t necessary for git basics, but it was invaluable for using advanced git commands we needed to use for our production environment, and figuring out how to design the architecture of our primary code repository.
A lot of work, but it was worth it – git really is a great tool.
I have an off-topic question one of you Unix shell scriptors might be able to help me understand.
Some time ago, after I installed an application (macOS I don’t remember which, Intel) in System Settings / Login Items & Extensions / App Background Activity I was asked to give “sh” permission to run in the background as a startup item. sh was identified only as an “Item from unidentified developer”. Even though I do not know much Unix, I figured this was a Unix shell script. Show In Finder confirmed sh is located in Macintosh HD / bin / along with other standard scripts. It seemed really strange to be asked to give permission for a system script to run. Why would I need to give permission for a part of the System to run? More troubling, why would the request come from an unidentified developer?
I reported this to Apple as a bug and a security issue, but of course got no reply. So far I have followed what I believe to be good security practice, and not given permission for this system script from an unknown developer to run in the background at startup. I suppose some developer somewhere thought I would benefit from running ls. I hoped Apple would ask the developer to find a way to request this without asking for what appears to be a bad practice with respect to security. No response from Apple or anyone for several years now.
There are certainly those more expert than me on this forum, but from reading the man page, sh is part of the standard macOS install, and serves as a pointer to whatever shell you have designated as your default (bash, dash, or zsh, specifically). So a process on your Mac that wants to run a shell command without specifying a particular shell can run sh, which just points to your preferred shell. I believe any time an application wants to install a background task you will be prompted to allow it. That’s not unusual, and doesn’t have anything to do with what that particular task does or uses. Apparently the app you installed wanted to run a shell command as a background task, and so you were asked to permit it to do so. Whether or not you should, of course, is a different question, but I don’t think there’s anything unusual about sh being involved here.
Yes, the sh command is a part of the operating sysftem. But it makes sense to require authorization. Not because sh itself could be malware, but because the script you’re running through it might be.
Apple’s own system services won’t require this special permission, even if they are using sh. My guess is that whatever app you installed is using a sh script as a part of its auto-launching. Maybe to set up a runtime environment before starting the real app.
I don’t think this is a bug. And I don’t think the notification should mention Apple as the developer. Both because Apple didn’t develop the shell (it came from the BSD Unix on which macOS is based, which is from a huge pool of authors), and because the real potential security issue is in the script that is to be run, not in the shell itself.
Thanks for the enlightenment. This is basically what I thought. It seems to me that a legitimate developer who needs me to allow a script to run should be identified. A request from an unidentified source to bypass Apple security and allow a script to run, which possibly enables other unknown scripts or who knows what, could be a classic example of breaking security by social engineering. Until I find out what this is about I will not allow it to run.
I hoped Apple would recognize the issue and notify the developer to use a less shady-appearing way to ask for sh to be enabled at startup. Apple accepting and allowing this example of poor software design may lead users to adopt insecure habits.