Category Archives: Terminal
fastest way to reboot your mac

If you need to teardown your current login session and get your mac up and running in a hurry, here’s a neat little trick that should take you from login to logout and back again in as little as 10 seconds. And I’m not talking about SSD-only Macbook*’s either. That’s the time I achieved on a clunky late-2014 iMac with a sluggish old mechanical 1TB Fusion drive.
Forget the Apple menu and the ‘Restart…’ menu option and don’t worry about how many login items or startup scripts you’ve got either, this method will slice through them all.
We’re going to leverage a little-known command in the launchctl
command line tool that will teardown and rebuild your login sessions in an instant. To see for yourself, execute this in Terminal:
sudo launchctl reboot userspace
I was astonished at just how fast the reboot is. What’s even nicer is you don’t lose any unsaved data either. This command restores all current window sessions (similar to checking the ‘Reopen windows when logging back in…’ option in the normal Restart dialog) but without the delay of the standard restart procedure.
See the man launchctl page for more options on using its reboot
command.
Enjoy. 🙂
featured picture: Steampunk boots by Imp0s5ible
how to find when the login password was last changed

Sometimes it can be useful to know when the user’s password was last changed. For example, you might want to enforce a policy of having users (or yourself!) change login passwords after a given period. Alternatively, if you or one of your users is experiencing login difficulties, you might want to check that the password hasn’t been changed unbeknownst to (or unremembered by) the user.
We can accomplish this from the command line (aka by using the Terminal.app) with the following one-liner (a raw text version is also available from my pastebin here):
echo; echo Password Last Changed:; u=$(dscl . list /Users | egrep -v '^_|daemon|nobody'); for i in $u; do printf \\n$i\\t; currentUser=$i;t=$(dscl . read /Users/"$currentUser" | grep -A1 passwordLastSetTime | grep real | awk -F'real>|</real' '{print $2}'); date -j -f %s "$t" 2> /dev/null; done
Note the odd entry belonging to user ‘dev’ in the screenshot: the 1970 date is the start of unix time, and its appearance here indicates that the password hasn’t been changed since time began!…or, more seriously, that this password hasn’t been changed since the user account was created.
Enjoy! 🙂
scan for malware on the command line

DetectX Swift now has the ability to do command line searches for issues on your mac like malware, keyloggers, browser hijacks and potentially dangerous software, and there’s a number of extra options that are not available when using the user interface. In this post, I’m going to give you a quick tour of the CLI (Command Line Interface) tool with some examples of how to use it (if you haven’t yet grabbed a free copy of DetectX Swift you might want to do that first to play along).
1. Basic scan
Let’s start with a basic scan. To use the CLI search, you need to specify the full path to the app executable. In this example, let’s suppose that the app is in /Applications folder. In that case, you’d need to execute this on the command line:
/Applications/DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift search
Since that’s a bit of a handful, even using tab completion, you might want to edit your .bash_profile to include a shortcut alias. Here’s mine:
sphil@sphils-iMac-5:~$ cat .bash_profile
alias sudo='sudo '
alias detectx='/Applications/DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift'
Note the sudo line (and note the extra space in the value). We’re going to need that so that we can pass the alias to sudo when we want to pass certain options to the search. Like…
2. Scan other users
Probably the most important benefit you gain with scanning on the command line rather than from the app’s interface is the ability to scan all, or selected, other users. You can search all users by using sudo
and the -a
option:
sudo detectx search -a
If you want to restrict the search to one or more users, the -u
option allows you to specify a list of shortuser names (comma-delimited):
sudo detectx search -u alice,bob
3. Go deep
If you’d like more verbose output, including how long the search took, try either the vsearch
or vvvv
commands:
sudo detectx vvvv -a
4. Save the results
You can specify a path to output the results, either in regular text:
sudo detectx vvvv -a ~/Desktop/searchtest.txt
or, by passing the extra -j
option, in JSON format:
sudo detectx search -aj ~/Desktop/searchtest.json
Here’s an example of what the formatted JSON file looks like:
5. Anything else?
There’s a help
command that will output the documentation to the command line, and also if you get into the habit of regularly running command line checks, don’t forget to launch the app from time to time in the Finder. Like its predecessor, DetectX, DetectX Swift does a lot of other stuff besides searching that can help track down and remediate problems with your mac, and a large part of that revolves around the way it tracks changes to your system every time you launch it. The CLI tool runs independently of that and won’t give you that kind of feedback or record those changes.
Finally, note that in the release version of DetectX Swift, the CLI tool is only available for a limited period to Home and Unregistered users. Unlimited acccess to the CLI tool requires a Pro or Management license.
Enjoy! 🙂
learning the Terminal : Part Four

One of the obstacles in becoming a command line guru is actually figuring out not just what’s available (see learning the Terminal: part Three) but how to use it. That just got a whole lot easier thanks to a (relatively) new tool called tldr.
Most command line (CLI) utilities come with either a man page or a help command, invoked either by man
or </path/to/tool> --help
, respectively. That’s traditionally the way we learn all the ins-and-outs of a given tool. If you’re lucky, there will be some examples at the end of the man page, and if you’re luckier still, there’ll be an example of exactly what you want to do with the tool there, too.
But what if you’re not lucky? Some CLI tools come with very few or no examples, some come even without a help command or man page. When that happens, prior to tldr your best bet was to go on an internet search or ask a friendly CLI expert if you knew one.
Now, tldr provides a third, and perhaps better, option. Unlike traditional man pages, tldr just spits out some basic examples of the CLI tool you specify, and that (more often than not) may be all all you need to get going (see the screenshots for examples).
If you have brew installed, you can get tldr easily with the command
brew install tldr
If not, either install homebrew first, or see the tldr homepage for other ways to install.
Using it is simple. Try some of these for fun:
tldr find
tldr awk
tldr sed
tldr cut
and so on. Here’s the output for tldr grep
:

The one thing tldr doesn’t seem to have built in that I can find is a list of the commands it knows about. There is a workaround, though. You can search through the tldr pdf to see what’s available. Alternatively, we can us a bit of command-line magic to do it for us:
find ~/.tldrc -path '*common*' -or -path '*osx*' | cut -d"/" -f8 | cut -d"." -f1 | sort
If you’d like to make that into a nice little function that you can call simply with something like tldr_list
, review the first post in this series on how to make and add functions to your .bash_profile
.
As tldr is a community-based program, it is likely to be added to quite regularly (I have a few I’m planning to contribute myself), so we can only hope that the pdf is updated regularly and/or that tldr gains a proper introspective list function.
Speaking of updates, if you try tldr on itself, i.e,
tldr tldr

you’ll note the ‘update’ command. It’s probably worth remembering to run that from time to time. tldr also comes, of course, with its own man page, too!
Enjoy! 🙂
Related Posts
learning the Terminal – Part One
learning the Terminal – Part Two
learning the Terminal – Part Three
Terminal tricks for defeating adware

So, your browser is acting up, redirecting you to scamsites, offers for MacKeeper and Mac Cleaner and other unwanted software. You have what is technically known as ‘an adware’ infection. It’s not a virus, it’s not a ‘trojan’ and it’s not a ‘worm’, but it is a nuisance and may well be associated with any of the above. What to do?
Here’s 10 Terminal tricks you can use to help identify and remove adware items. It won’t cover every situation: adware mutates faster than a flu virus on amphetamines, but it will catch 90% of the cases currently out there. For the ones it doesn’t, see the ‘Getting Help’ section at the end of this post.
I’m going to split this up into two phases, ‘Gathering Information’ and ‘Dealing with the Results’. After explaining the first half-dozen commands individually, I’ll then give you one ‘master’ or ‘mother’ command which combines them into a single execution, but you should read through the explanations first so that you know what you’re doing and what to expect.
Gathering Info
First, most adware wants to persist on your mac across logins and restarts, and that means it has to put an executable somewhere where macOS will look on start up. One place most users should be familiar with and check first is the Login Items in System Preferences ‘Users & Groups’ pane. A lot of adware will insert itself there for good measure, but most will almost certainly be in other, trickier to find places.
This is where our first Terminal trick comes in. This Terminal trick will output the contents of the main locations where adware typically hides:
1. List the contents of your Launch* folders:
ls -alF /Lib*/Launch*/ ~/Lib*/Launch*/
That’ll output the contents of three different directories, /Library/LaunchAgents, /Library/LaunchDaemons, and ~/Library/LaunchAgents. If you’re planning on getting help by publishing the results in a public forum like Apple Support Communities, then you might want to use this version, which will scrub your username out of the results:
2. Same trick, redacting personal info:
w=`id -un`;ls -alF /Lib*/Launch*/ ~/Lib*/Launch*/ | sed "s@$w@[redacted]@g"
The output of that command will have a load of files with names like ‘com.company.filename.plist’. To give you an example here’s what mine outputs (note, none of these are adware files; unsurprisingly, my Mac is adware free!).
Slipping a shell script into the /etc/
directory is a common adware trick, so let’s also run this one, which will output any files in /etc/
that have the .sh
shell script extension:
3. Find shell scripts in /etc/:
ls -alF /etc/*.sh
(this one won’t contain your user name, so we don’t need to redact anything).
A lot of adware persists by running sneaky little AppleScripts from a shell script. We can detect if any of these are at work with this little spell:
4. List osascript processes targeting your browser:
ps -axo ppid,pid,command | grep 'osascript -e global' | egrep -i "if is_Firefox_running|if is_Safari_running|if is_Chrome_running" | grep -v "grep" | grep -v ' 1 ' | awk '{ print $1, $2}'
All this command outputs is two numbers, perhaps like this:
7783 7792
7783 7825
8978 8987
We’ll discuss what to do with those in the ‘Dealing with Results’ section below.
Next, we want to see what processes are actually running in the background. This will both confirm and possibly add to information we collected earlier. To do this, we need a little trick which looks like the same command twice, but which in fact operates on two different lists of processes:
5. List loaded background processes:
w=`id -un`; r="s@$w@[redacted]@g"; launchctl list | grep -v apple | sed "$r"; sudo launchctl list | grep -v apple | sed "$r"; sudo -K
When you run this one, a list of processes will be output, and then you’ll be asked to supply an Admin password on the command line (where even the key presses won’t be visible when you type). Supply the password and a second list will be produced. We will want to examine both later.
A file name common to a widespread family of adware is rec_script.sh
, and this can be hidden anywhere in the user or local Library folders, so let’s run this one, too (here we will add the redacting again in case you’re posting the results in a public forum). You’ll need to supply an admin password for this though:
6. Find a common adware executable:
w=`id -un`; sudo find /Library ~/Library -name "*rec_script.sh*" | sed "s@$w@[redacted]@g"; sudo -K
This one may take a couple of seconds to complete.
That concludes the first step of our info gathering stage, but for convenience, I’m going give you them all again concatenated into one, single ‘mother of all commands’ 🙂 string. Even more conveniently, I’ve added code to output the results to a text file on your Desktop, called ‘adware_search.txt’, so after running the code below go look for ~/Desktop/adware_search.txt in Finder. If you’re posting to a public forum, it’s much easier to copy and paste the results from the text editor rather than from Terminal.
TL;DR
If you triple-click anywhere in the block of code below, you can copy and paste the whole block into Terminal and execute all of the commands given above in one fell swoop. Remember you’ll need a password.
7. The ‘Mother’ of all the above:
w=`id -un`; r="s@$w@[redacted]@g"; f="/Users/"$w"/Desktop/adware_search.txt"; ls -alF /Lib*/Launch*/ ~/Lib*/Launch*/ /Users/Shared /usr/local/bin | sed "$r" >> "$f"; printf "\n\n/etc:\n" >> "$f";ls -alF /etc/*.sh 2>/dev/null >> "$f"; printf "\n\n# osacript processes:\n" >> "$f"; ps -axo ppid,pid,command | grep 'osascript -e global' | egrep -i "if is_Firefox_running|if is_Safari_running|if is_Chrome_running" | grep -v "grep" | grep -v ' 1 ' | awk '{ print $1, $2}' | sed "$r" >> "$f"; printf "\n\n# User launchd:\n" >> "$f"; launchctl list | grep -v apple | sed "$r" >> "$f"; printf "\n\n# Root launchd:\n" >> "$f"; sudo launchctl list | grep -v apple | sed "$r" >> "$f"; printf "\n\n# Find rec_script.sh:\n" >> "$f"; sudo find /Library ~/Library -name "*rec_script.sh*" | sed "$r" >> "$f"; sudo -K
Interlude: Playing Safe
Before we move on to dealing with the results, I want to stress that you don’t want to be deleting files that you’re not sure of. Good practice is to move files to a temporary Quarantine folder, or at least move them to but don’t empty the Trash.
Even better practice is to make sure you have an up-to-date, bootable backup disk as well as a Time Machine backup, so that you can easily recover your system if you make a mistake and delete something you shouldn’t.
Dealing with the results
Looking at the output of the first Terminal command given above (Trick 1 or 2), how can you tell which are good and which are bad? In a lot of cases, you’ll recognise the app or developer name. TunnelBear, for example. “Sure, yeah, I’ve got that” and so on. Others, however, will look and sound weird, like these (all genuine adware file names):
com.glutting_Panagia.plist
com.pPHGASlN.plist
com.phellonic.plist
Google anything you’re not sure of, and see if it’s already been identified as adware. See ‘Getting Help’ at the end of this post if you’re not sure.
Walking up & down the tree
Assuming you’ve found some candidates for removal, the next job is to find the parent and child processes associated with each. We do that with a couple more Terminal tricks.
For the first one, we want to find any process that contains the same name as our suspected adware. For each suspect, take the unique part of the name for your search term. With this one we can put all our candidates in one command like so:
8. Search for your target’s family members:
w=`id -un`; ps -axo ppid,pid,command | egrep -i "glutting_Panagia| pPHGASlN | phellonic" | grep -v ' 1 ' | grep -v grep | sed "s@$w@[redacted]@g"
Note the part after egrep -i
that’s inside quote marks. Each search term is separated between a vertical bar, aka the pipe character. Note that the terms themselves are not inside quote marks individually. One pair of double-quote marks is used to encapsulate all terms.
So to use the command above replace “glutting_Panagia| pPHGASlN | phellonic” with “search term 1 | search term 2 | search term 3”, where ‘search term n’ is your search term. Of course, you can have more or less than three search terms. Just add or remove as required.
When you examine the results, so long as the first number is not ‘1’ (it shouldn’t be if you executed the command correctly, as all those should have been excluded), follow the file path shown under the ‘Command’ column using either Finder or the Terminal. If you’re sure you’ve found a baddie, send it to the Trash or your quarantine folder! If you’re not sure, see ‘Getting Help’ below.
You will need to construct and run the next command separately for each suspect. The output will give you the path to the binary being executed by the plist. In many cases, you’ll have already found that from the previous commands, but in some cases – particularly if the plist has failed for some reason or if the binary isn’t running when you do your search – it won’t. This one’s the trickiest because you’re going to have to construct most of it yourself. Here’s an example (this is actually a legitimate file, but it will serve for our purposes):
cat /Library/LaunchAgents/com.razer.rzupdater.plist | grep -iA3 program
Let’s look at how that command is structured:
9. Find more children:
cat [path to file] | grep -iA3 program
You get the ‘path to file’ part from the results of your /Library/Launch* searches, and there’s no harm in practising this one on good files to get used to the output. For each item you search, it should return something that looks like this:
Here we see the path to the executable that the plist is launching. If this were a bad guy, I’d be straight over there to send him where he belongs, too.
After working through all your suspects with Trick 8, now take a look at the results of the command to output shell script file names from /etc/ (Trick 3). If there were any results at all (hopefully there wasn’t), you’re going to have to open that file in a text editor and determine whether it is malicious or not. This is the hardest part for the novice, because there’s certainly plenty of reasons to have a shell script in /etc/ depending on what 3rd party software you’re running. I can only repeat here what I have said above: see the ‘Getting Help’ section below if in any doubt.
Next, let’s take a look at the results for the osascript processes (Trick 4). Hopefully, you got no results, but if you had two sets of numbers outputted like this:
7783 7792
then the first number is the parent process ID, and the second number is the child ID. We want to find and eliminate both the parent (again, so long as this number is not ‘1’) and the child.
Take the first number and execute this in Terminal
10. More parents on the loose:
ps [number]
Take a note of the path that’s shown and ensure it doesn’t belong to a legitimate app that you recognise. Again, if in doubt, ask Google, or see ‘Getting Help’ below.
Now, do the same with the second number, the child process. Work through however many numbers were output, ‘quarantining’ as you go.
Almost there! Take a look at the output of the two launchd lists (Trick 5). You should have a good idea by now which ones are suspect and which are OK. You may have found the paths to any suspicious ones already, but if not, we’ll use the same command as we just used with the osascript processes. Here’s the output on my machine of the Trick 5 command (all legitimate) for comparison:
We’re only interested in the first number (the second one is status code). For any suspicious process, take the first number shown in the list, and use the Trick 10 command on these to find the parent file path (you know what to do with the ones that aren’t legitimate!).
If there is only a ‘-‘ dash instead of a number, it means that process is or was loaded but is not currently running. That dash may or may not be followed by another number that is not ‘0’. That number is just an error code and isn’t really relevant to us here. For any of your suspects that have failed like that, hopefully the info you gathered earlier will give you some clues (if not, see ‘Getting Help’ next).
Finally, anything found in the ‘find’ command (Trick 6) is almost certainly malware. Of course, be mindful that it’s entirely possible a legit’ script could accidentally have a name clash and be called rec_script.sh
, but it’s highly unlikely and definitely should be examined closely. Also, if you see that the path is within an application bundle like this …Contents/MacOS/rec_script.sh
, don’t hesitate to pull the trigger on that one.
Getting Help
I want to repeat that doing this safely and effectively takes practice and experience, and you should in no way be surprised that, if you don’t have that experience, you’re not sure whether something you’re looking at is good or bad, or that you go through all of this and still can’t find the problem. There’s some fairly obscure ways that adware and other malware can infest and persist on your mac that only experts will be able to advise you on. Throughout this post I’ve glossed over a few situations where you’ll draw a blank, and that’s because most of the other techniques for spotting malware require that experience.
To ameliorate this, I wrote an app called DetectX Swift to deal with this and many other things, and you can download it and use it without any requirement to pay. You can also use it to get personal, free-of-charge, help from me through the Help > Report a Problem to Sqwarq Support if your troubles persist.
Let me be clear why I don’t charge for this personal service: the payoff for me is I get to improve my app’s heuristics from what I learn about infections that DetectX doesn’t automatically detect. All information is kept strictly confidential and I do not sell or use your email address or other information for any marketing purposes whatsoever.
If you want to read more about me, also see the about page on DetectX’s parent site, Sqwarq.com.
Happy hunting! 🙂
Related Posts
scan for malware on the command line
how to reveal hidden users

With malware big in the news again, and evidence that at least one malware variant that targets macOS creates hidden users on the victim’s system, here’s a timely tip on how to check for unwelcome guests.
For this tip, we’re going to use the Terminal, which you can find in the /Applications/Utilities folder. If you’re not a frequent visitor to the land of the command line, you might want to see my 3-part series “Learning the Terminal”.
Regardless, the first thing we’re going to do in Terminal is about the simplest command you’ll ever type: w
. Yep, type a single ‘w’ at the prompt and press return.

The w
utility is a very quick way to see who’s currently logged on to your system and to ensure that there’s no surprises. You should see a couple of entries for yourself: one as ‘console’ and one as ‘s***’. The first represents a login through the usual Desktop GUI login window; the second is there because you just logged into Terminal. Anybody else logged in either via the command line (like a potential remote user) or the GUI will show up here. Notice that on my machine, there’s another user called ‘Developer’ who hasn’t logged in using the GUI, but is logged in via a command line interface. Note that ‘w’ returns the full user name, not the short one.
While the w
utility will tell you if a hidden user is currently logged on, what if there’s a hidden user that isn’t active at the particular time you check? To look for those, we have a couple of options. First, we can use the dscl
utility to list all users, and you might be surprised at how many there are:
dscl . -list /Users
Look to the end of that list where the names that don’t begin with an underscore start. ‘Daemon’, ‘Nobody’, ‘Root’ and ‘Guest’ are all standard system accounts, as are all those entries that begin with an underscore. Don’t worry about those. However, aside from those, you should only see names that you recognise. To make things a little easier, we can add another command to the dscl command to filter that list. Try this
dscl . -list /Users | grep -vE ‘_|root|nobody|daemon|Guest’
That should now only return the names of real users. There shouldn’t be any names in there you don’t recognise. In my example, I know the last three, but the first one ‘dev’ isn’t familiar to me. Note that unlike ‘w’, this command returns short user names, and that ‘dev’ looks very much like it’s the same account as ‘Developer’ that I saw earlier.

However, what we have so far is a list of users, not a list of hidden users. To see specifically if any accounts are hidden, we need a longer command:
defaults read /Library/Preferences/com.apple.loginwindow
Normally, when there are no hidden users, this will return the contents of a property list file that may look something like this:
{
GuestEnabled = 1;
OptimizerLastRunForBuild = 31898816;
OptimizerLastRunForSystem = 168494592;
SHOWFULLNAME = 1;
lastUser = loggedIn;
lastUserName = imackim;
}
That tells us that there’s no hidden users on this mac. How so? Because if there were it would return something very different, like this:

We can see not only the list of hidden users, but also that the preference for hiding users has been set to ‘1’ (in plist syntax, ‘1’ means true and ‘0’ means false). Note again that unlike the dscl
command above, this returns the account’s full name, not the short user name.
If we’d like to ‘unhide’ that user, so the account appears in the login window GUI and in System Preferences’ ‘Users & Groups’ pane, we’ll need admin privileges. To do that, cut and paste the following into Terminal:
sudo defaults write /Library/Preferences/com.apple.loginwindow Hide500Users -bool NO
Supply an admin user password at the prompt and hit ‘return’, but type slowly as the display doesn’t register your key presses, which makes it easy to fat finger your password.
Next, we need to change the IsHidden
key in dscl
for that user, so
sudo dscl . -change /Users/dev IsHidden 1 0
In the above command, substitute my user’s shortname dev
for the shortname of the user on your machine. The 1 0
is the old value (hidden) followed by the new value (unhidden). The line can be read as “change User dev’s IsHidden value from hidden to unhidden.”
For the more advanced
We can save ourselves some typing by putting much of this into a script so that we can run it whenever we want. If you’re not familiar with how to create and use bash scripts, take a look here.
Our script will basically do the same as all the commands we listed above (except changing the prefs for Hide500Users
) in one fell swoop, and there’s a couple of little twists that I’ll leave as an exercise for the reader to figure out. To save on the typing, you can copy the whole script from my pastebin here.

The script’s output is illustrated in the shot at the top of this post.
Enjoy! 🙂
how to fix a ‘file in use’ problem

Sometimes when you try to eject a disk, unmount a volume or empty the Trash, you get caught out by some app or process that’s using the file and won’t release it. This is usually signalled by a warning dialog telling you the said file is “in use” or is “locked”.
Part of the difficulty of dealing with this problem is that the warning message may not actually tell you which process is hanging on to the file or give you any options on what to do next to solve the problem.
Sounds like a job for a quick bit of bash scripting then!
We’ll write a one-stop script that leverages a few different command line utilities to help us out here. First, our script will call fuser
to report the processes using the file. Then it’ll use ps
to get those processes’ ID numbers and, after asking us to confirm what we want to do, it’ll feed those to the kill
command to quit them and release the file.
The whole script is available here.
To use it, save the script as a plain text file in the root of your home folder (alternatively, save it in an /sbin
folder. You can do echo $PATH
on the command line to get a list of places you can save it to if you’re not sure).
Secondly, give it executable permissions with
chmod +x <script name>
When the problem strikes, jump into Terminal and type
./<script name>
Add a space, then type or drag the file from Finder onto the command line and hit ‘return’ if necessary. The script will do the rest.
In the image below, I first gave my script (named ‘releaseFile’) exec permissions. Then I called it and chose ‘a’ to quit all processes holding on to the file (in this case, only one process).

Hope that helps. Enjoy! 🙂