…or, how to deal with deprecated bash and python…
There are many solutions to get the current logged in user in macOS to use in a shell script. However, the semi-official, “sanctioned” method has always involved a rather elaborate python one-liner, originally published by Ben Toms:
loggedInUser=$(/usr/bin/python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");')
Update, because this a FAQ:
There are various other solutions to get the current user which use
stat
,who
, orlast
commands. These will work in most situations. But there are edge cases, mostly with Fast User Switching, where these methods don’t return the correct user.
From python to scutil
While this has worked wonderfully and more reliably than other solutions, it always looked inelegant, and added a dependency on the python
binary. This used to be fine, as the python
binary shipped with macOS. But in the macOS Catalina release notes, we have learned that some, yet undefined, future version of macOS will not include the Python, Ruby, and Perl interpreters any more.
With prescient timing, Erik Berglund figured out a different method, which still accesses the same system framework, but through the scutil
command rather than the PyObjC bridge.
Over time, different contributors in the MacAdmins Slack have optimized this command:
loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
Sidenote: the
scutil
version runs 15–20x faster than thepython
version. Speed is not usually a major consideration for MacAdmin scripts, but it’s a nice improvement.
From bash to sh
This works great in bash
and zsh
. However, the built-in bash
has been deprecated as well. (I have talked about this, a lot.)
While zsh
is a great replacement for bash
for interactive terminal use and scripting on the ‘full’ macOS system, there are environments (Recovery) where zsh
is not present.
The only shell Mac Admins can rely on in all contexts is now /bin/sh
.
Because of this I recommend using /bin/sh
for installer scripts (pre- and postinstall scripts in pkgs).
The Posix sh
standard does not include the ‘here string’ <<<
used in Erik’s command. Nevertheless, when you use the above construct in sh
on macOS it will still work fine. This is because sh
on macOS is actually handled by bash
in sh
compatibility mode.
And while bash
in sh
compatibility mode will recreate the odd syntax and quirks of sh
for compatibility, it will also happily run ‘bashims’ that don’t even exist in sh
. E.g. double brackets [[...]]
or here strings <<<
.
As long as bash
is taking care of sh
scripts, you will be fine. Myself, I would not have noticed this if I had not ran a sh
script through shellcheck
. Shellcheck told me that Posix sh
should not understand the here string:
loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
^-^ SC2039: In POSIX sh, here-strings are undefined.
But we’re fine on macOS, because sh
is really bash
right? No need to worry…
Predicting the demise of bash
We don’t yet know when, but the /bin/bash
binary will eventually be removed from macOS. The version included with macOS has not been updated since 2014. It is unlikely that the twelve year old bash
v3.2 will receive patches for security vulnerabilities in the future.
With the switch to zsh
as the default shell in Catalina Apple is laying the groundwork to remove bash
v3.2 from the system in a future release. cron
and login hooks were infamously deprecated in macOS years ago and are still around, so we could be looking at years. But my suspicion is that bash
is just one security vulnerability away from being removed.
With the High Sierra and Mojave updates, Apple has shown that they do not need to wait for major releases to remove or add functionality or restrictions to the system.
I believe /bin/bash
has more than a few months still. However, Apple’s messaging on this switch is uncharacteristically strong. It would not be terribly surprising if the Catalina Spring Update in March 2020 removed bash
.
The amount of old installer packages which would break on such a change is terrifying. Apple might be willing to pay this price, to avoid a publicized security vulnerability. A severe bash
vulnerability would definitely get a lot of publicity. Remember Shellshock? That was the last time bash
v3.2 was patched.
Instead of removing /bin/bash
Apple might be able to replace it with zsh
in bash
emulation mode. So far, I have found no signs for this. While it wouldn’t be perfect, it would be a safer move.
Python 2.7 will still get updates until the end of 2019. There are also some commands and tools in the system written in python 2.7. It will be harder for Apple to migrate these to other languages, so I think Python has a longer countdown clock. But it cannot hurt to start preparing now.
Whither sh?
Whenever /bin/bash
is removed, what will happen with /bin/sh
at that point? The answer can be deduced from the support article on the shell switch.
How to test your shell scripts
To test script compatibility with Bourne-compatible shells in macOS Catalina, you can change
/var/select/sh
to/bin/bash
,/bin/dash
, or/bin/zsh
. If you change/var/select/sh
to a shell other than bash, be aware that scripts that make use of bashisms may not work properly.
zsh
can be made to emulate sh by executing the commandzsh --emulate sh
.
We can actually see that Apple has two options here. sh
processes could be run by zsh
in emulation mode or by dash
.
dash
is the Debian Almquist shell, which is a minimal implementation of the Posix sh
standard. dash
is a newcomer to macOS on Catalina. New arrivals on a release where Apple seems intent on cleaning out ballast (32-bit, Kernel Extensions, bash v3, Python, Perl, and Ruby) are immediately of interest.
When you check on Catalina Recovery system, you can see that /bin/dash
has been added, but /bin/zsh
is absent. (/bin/bash
is still present on Catalina Recovery and the sh
binary is also still bash
.)
Putting all of this together, I would hazard that Apple is planning to use dash
to take over from ‘bash
as sh
.’ This will also bring macOS in line with most other Unix and Linux distributions where dash
commonly handles sh
processes.
With the symbolic link mechanism described in the support article, Apple could switch sh
to dash
in an update while bash
is still present. This could allow Developers or Mac Admins to switch their managed systems back to bash
when they need to mitigate problems.
When Apple switches to dash
, sh
scripts that use bashisms will break.
You can start testing this in Catalina by changing the symbolic link at /var/select/sh
:
% sudo ln -sf /bin/dash /var/select/sh
(Change /bin/dash
to /bin/bash
to revert to default.)
When I test the above command to get the current user with dash
, it returns:
Syntax error: redirection unexpected
(You can also change the shebang of a script you want to test to #!/bin/dash
then you do not have to change the system sh
.)
Getting the Current User in sh, Future Proof (I hope)
The solution is easy enough. Replace the here string with a pipe. For sh
scripts, change the syntax to:
loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
This will make Posix sh
, dash
, and shellcheck
happy and will still work in bash
and zsh
.
This syntax (echo
ing into a pipe) is considered inelegant and inefficient, because it requires an additional process (echo
) and an additional pipe. Here docs and here strings were introduced exactly to avoid this. Obviously, if you prefer, you can continue to use the here string form of the command for bash
and zsh
.
Conclusion
There is no need panic and search and replace the python command in all your scripts immediately. I would recommend replacing this line when you update a script. Keep all of this in mind when working on your administration, installation and workflow scripts going forward.
You may also find scripts in third party documentation and installers that need updating, and you should let the author or vendor know.
/bin/bash
(v3.2) is deprecated and has not received updates since 2014/usr/bin/python
(v2.7) is deprecated and will not receive new updates starting in Jan 2020- both will eventually be removed from macOS
- we don’t know when exactly, could be years, could be months
- even when you do not use Python for scripting, you may be using Python ‘one-liners’ in shell scripts
bash
scripts should be moved tozsh
(general use) orsh
(general and installation scripts)sh
processes in macOS will likely be handled bydash
in the future- use
shellcheck
to find and correct bashisms insh
scripts shellcheck
does not work forzsh
scripts
Why would you recommend your command utilizing scutil over this command I see others recommending?
loggedInUser=$(stat -f “%Su” /dev/console)
I have a post on this here: https://scriptingosx.com/2020/02/getting-the-current-user-in-macos-update/
You’re the best!