This type of thing is usually done interactively, but when I’m piping output that doesn’t work. So here’s a quick one-liner in bash for pulling the TeamID and BundleID from kexts out of the KextPolicy sqlite database:
sqlite3 /var/db/SystemPolicyConfiguration/KextPolicy "SELECT * from kext_policy;" ".exit"
If you don’t already have the URL for an app, it can be obtained via a lookup using
If you’ll be performing these kinds of operations en masse from within server-side scripting, Apple has a number of programs, including the Affiliate Program, which allow you to do so more gracefully. But as a quick and dirty part of a script, this could solve a need. More importantly, hey, parse some json from bash without piping to python or perl or whatevers… Enjoy!
The sparkr gem is installed by default in macOS. To use it to produce simple graphs, simply run it followed by a series of integers:
sparkr 12 110 250 110 12
The result would be as follows:
This is useful for a quick and dirty visualization in scripts. For example, a series of 5, 10, 200 numbers that don’t have that much range where you’re just looking for a simple pattern. Like number of lines in logs, etc. Obviously, you can pay a lot of money for graphing frameworks and very fancy-schmancy tools. This is really just for me in small scripts.
Note: sparkr isn’t installed on all Mac systems. to install it manually use:
sudo gem install sparkr
Thanks to Armin Briegel for pointing out that sparkr isn’t installed by default on the latest OSen.
The colrm command is a simple little command that removes columns from standard input before displaying them on the screen (or piping the text into another file). To use, simply cat a file and then pipe it to colrm followed by the start and then stop in $1 and $2. For example, the following would only list the first column of a text file called testfile:
cat testfile | colrm 2
Not providing a second column in the above command caused only the first column to be displayed to the screen. You could pipe all but the second and third columns of a file to another file called testfile2 using the following:
One of the most important skills in any language is to know how a basic if then statement works. You set a variable, you say if that variable matches a given pattern (or doesn’t) then do something or else do something different. If/then/else. Other than that, it’s just syntax, and the annoyance of figuring out the operators and syntax.
So let’s go through the below script (which checks a Mac to see if grayscale is enabled in Universal Access), line-by-line:
Line 1: we’re first going to create a variable called grayscale. We’ll fill it with a 0 or 1, the output of a simple “defaults read com.apple.universalaccess grayscale” command
If that grayscale variable matches 0 execute whatever is after the then
One line just for then followed by the command in the next line (which will optionally allow for an else)
Use echo to display the text on the screen that indicates Grayscale is disabled to stdout (which could be redirected if you want to do something after we’re done processing)
One line just for the else (optional)
The echo to display it’s enabled
fi ends the if/then so you can move on to the rest of the script (even if you have nothing after the if/then you’ll need the fi or the script won’t run)
grayscale=`defaults read com.apple.universalaccess grayscale`
if [[ $grayscale = 0 ]] then
echo "Grayscale is disabled"
echo "User doesn't have a token"
In the above if, we used = as an expression. That’s the most common one you’ll likely use, but there are a lot, including the following:
[ -a FILE ] True if FILE exists
[ -b FILE ] True if FILE exists and is a block-special file
[ -c FILE ] True if FILE exists and is a character-special file
[ -d FILE ] True if FILE exists and is a directory
[ -e FILE ] True if FILE exists
[ -f FILE ] True if FILE exists and is a regular file
[ -g FILE ] True if FILE exists and its SGID bit is set
[ -h FILE ] True if FILE exists and is a symbolic link
[ -k FILE ] True if FILE exists and its sticky bit is set
[ -p FILE ] True if FILE exists and is a named pipe
[ -r FILE ] True if FILE exists and is readable
[ -s FILE ] True if FILE exists and has a size greater than zero
[ -t FD ] True if file descriptor FD is open and refers to a terminal
[ -u FILE ] True if FILE exists and its SUID (set user ID) bit is set
[ -w FILE ] True if FILE exists and is writable
[ -x FILE ] True if FILE exists and is executable
[ -O FILE ] True if FILE exists and is owned by the effective user ID
[ -G FILE ] True if FILE exists and is owned by the effective group ID
[ -L FILE ] True if FILE exists and is a symbolic link
[ -N FILE ] True if FILE exists and has been modified since it was last read
[ -S FILE ] True if FILE exists and is a socket
[ FILE1 -nt FILE2 ] True if FILE1 has been changed more recently than FILE2, or if FILE1 exists and FILE2 does not
[ FILE1 -ot FILE2 ] True if FILE1 is older than FILE2, or is FILE2 exists and FILE1 does not
[ FILE1 -ef FILE2 ] True if FILE1 and FILE2 refer to the same device and inode numbers
[ -o OPTIONNAME ] True if shell option “OPTIONNAME” is enabled
[ -z STRING ] True of the length if “STRING” is zero
[ -n STRING ] or [ STRING ] True if the length of “STRING” is non-zero
[ STRING1 == STRING2 ] True if the strings are equal. “=” may be used instead of “==” for strict POSIX compliance
[ STRING1 != STRING2 ] True if the strings are not equal
[ STRING1 < STRING2 ] True if “STRING1” sorts before “STRING2”
[ STRING1 > STRING2 ] True if “STRING1” sorts after “STRING2”
[ ARG1 OP ARG2 ] “OP” is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return true if “ARG1” is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” are integers
Builtin commands are always kinda’ interesting. At first glance, it’s hard to know which commands are builtins. Luckily, there’s a command that I rarely use, called… command. If you run command with the -V flag it will tell you if the command is a builtin:
command -V cd
cd is a shell builtin
If you run a command that isn’t a builtin
command -V ls
ls is /bin/ls
Some builtins are in /bin (like echo). But not all builtins are in /bin. Some are in /usr/bin (like cd). Information about how to use builtins is built into the help command rather than standalone man pages. So, if you do help followed by the name of a command, you’ll get information about the command, and sometimes how to use the command:
cd: cd [-L|-P] [dir]
Change the current directory to DIR. The variable $HOME is the
default DIR. The variable CDPATH defines the search path for
the directory containing DIR. Alternative directory names in CDPATH
are separated by a colon (:). A null directory name is the same as
the current directory, i.e. `.’. If DIR begins with a slash (/),
then CDPATH is not used. If the directory is not found, and the
shell option `cdable_vars’ is set, then try the word as a variable
name. If that variable has a value, then cd to the value of that
variable. The -P option says to use the physical directory structure
instead of following symbolic links; the -L option forces symbolic links
to be followed.
There are also commands not in a path, which can be found using the which command:
Recently someone asked me about accepting bash inputs. So I decided to take a stab at writing a little about it up. For the initial one we’ll look at accepting text input. Here, we’ll just sandwich a read statement between two echo commands. In the first echo we’ll ask for a name of a variable. Then we’ll read it in with the read command. And in the second echo we’ll write it out. Using the variable involves using the string of the variable (myvariable in this case) with a dollar sign in front of it, as in $myvariable below:
echo "Please choose a number: "
echo "You picked $myvariable"
Read also has a number of flags available to it:
-a assigns sequential indexes of the array variable
-d sets a delimiter to terminate the input
-e accepts the line.
-n returns after reading a specified number of characters
-p prompts without a trailing newline, before attempting to read any input
-r doesn’t use a backslash as an escape character
-s runs silent, which doesn’t echo text
-t: causes read to time out (number of seconds is right after the -t)
-u reads input from a file descriptor
Next, we’ll build on that read statement (note the addition of -p) and use a while to force a user to input a y or n and then parse their selection with a basic case statement:
read -p "Do you wish to continue?" yn
case $yn in
[Yy]* ) echo "Add your action here"; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
Finally, let’s look at positional parameters. Here, you can feed them at the tail end of the script, as words that are separated by spaces after the name of the script. Here, we simply just echo $0, which is the first position (aka – the name of the script you just ran) and $1 and $2 as the next two.
echo "You Used These"
echo '$0 = ' $0
echo '$1 = ' $1
echo '$2 = ' $2
You could also take $3, $4, etc. This is different than writing flags, which requires a bit more scripting. So if you called the script with:
You would see:
You Used These
$0 = ./pospar.sh
$1 = test1
What tips/additions do you have?
So I comment a lot of lines out in my /etc/hosts file. This usually means that I end up with a lot of cruft at the top of my file. And while I write comments into files and scripts here and there, I don’t always want to see them. So I can grep them out by piping the output of the file to grep as follows:
cat /etc/hosts | grep -v "^#"
You could also do the same, eliminating all lines that start with a “v” instead:
cat !$ | grep -v "^v"
When you’re regression testing, you frequently just don’t want any delays for scripts unless you intentionally sleep your scripts. By default Safari has an internal delay that I’d totally forgotten about. So if your GUI scripts (yes, I know, yuck) are taking too long to run, check this out and see if it helps:
defaults write com.apple.Safari WebKitInitialTimedLayoutDelay 0
With a script I was recently working on, this made the thing take about an hour less. Might help for your stuffs, might not.
If not, to undo:
defaults delete com.apple.Safari WebKitInitialTimedLayoutDelay