Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Swift, Shells In The 1960s, And Some Swift Scripting Examples For Admins

The reason Ken Thompson wrote the Thompson Shell (/bin/sh) when he and the team at Bell Labs developed Unix was that they didn’t want to have to teach programming to people in the patent office, who funded the PDP they used to write Unix.

Ken Thompson

Shell environments evolved over the years with tcsh, bash, and zsh to name a few. These added more concepts from programming environments, like the environment from C that the binaries they exposed were compiled in. Other languages emerged that were simpler than a language like C but added new techniques – and so perl, python, ruby, and others evolved. Some of those were either object-oriented from the outset or had object-orientation bolted on the side, like a FrankenLanguage. The addition of these shells to the Mac, gave those who wanted to automate tasks a simple interface to do so. Over time, shells have become more restricted, other scripting (or hybrid) languages removed from the Mac, and one alternative to shell scripting is now the ability to run a Swift script.

Swift scripts feel a bit more like procedural languages in their simplest incantations. They can be as simple or as complicated as we want them to be. However, whereas in bash there are dozens of standard scripting functions, simple pipes, process controls, and commands that we can call, we have to use pre-built APIs or functions to perform similar tasks with a language like swift. This is easier shown than said. Let’s start with a simple swift example, the old “hello world” script. For this, most will use the print Function (https://developer.apple.com/documentation/swift/print(_:separator:terminator:)) We’ll quote the string to print to the screen and, as with bash or zsh, begin the little script with a shebang line that call the swift environment to run the script:

#!/usr/bin/env swift

print("Hello Cruel World!")

Now let’s make it executable (assuming a name of HelloCruelWorld.swift):

chmod 750 HelloCruelWorld.swift

Then we can run it:

./HelloCruelWorld.swift

We could also have variabalized the words as follows:

#!/usr/bin/env swift

var firstWord = "Hello"

var secondWord = "Cruel"

var thirdWord = "World"

print(firstWord + " " + secondWord + " " + thirdWord + "!")

At this point, it’s worth pointing out that a function dates back to the era of lambda calculus when mathemeticians (most notably Alanzo Church in the 1930s) wanted a shorthand for a bunch of steps they might need to repeat ( the following image from https://medium.com/hackernoon/lets-code-the-roots-of-functional-programming-lambda-calculus-implemented-in-typescript-36806ebc2857 shows what that might look like:

The early programming languages built on this foundation and even often used the same symbols in lambda calculus. After the dawn of interactive computing, many (most notably Grace Hopper, but also John Backus with FORTRAN, etc) wanted to make computers more useful and programming more approachable by compiling these higher level functions into machine code. At the dawn of interactive computing and then timesharing, Bell Labs programmers were able to take the lessons learned from a generation of research, throw out the parts that Multics had stuffed in based on a “design by committee approach” and create C. C was so good it has only recently fallen to the 8th most used programming language of the day.

The syntax in simple swift scripts can be similar to that in C. For example, to print our hello world example to the screen in C with a function we’d use the following:

void myFunction();

int main() {

myFunction();

return 0; }

void myFunction() {

printf("Hello Cruel World!"); }

We can do the same in swift:

let secondWord = " Cruel World"

func hellocruelworld(person: String) -> String {

let theString = "Hello, " + secondWord + "!"

return theString }

The above is a little more condensed, but retains the parentheticals to denote an opening and closing stanza of objects to pass into the function, etc. We can avoid a step to void and init as swift takes care of some of that on our behalf. The printf is shortened to just print. But the similarities are there. Modern languages usually have lots of prebuilt functions, sometimes bundled into libraries or frameworks (or packages now). The trend here is that we’ll import frameworks that swift doesn’t come bundled with and call on functions from the frameworks for more complicated scripts. We’ll also want to use variables and constants in our scripts. Constants are immutable, so don’t change (mostly). Variables are, well, variable; they can change. So for a loop with maximums we might use the following:

let maximumNumberOfThings = 10

var currentNumberOfThings = 0

To add a tad bit of complexity, when we run the let, we can call a function and use the output of the function to supply that integer (or a string or array). We’re lazy typing. Before we set the contents of the variable we could have defined the type, like a String or an integer (Int in swift as follows):

var currentNumberOfThings: Int

With these basics in mind, let’s move on to what we often call “shelling out” a bash command from swift. We’ll again import Foundation, which gives us the Process() and Pipe() functions. The next section (most like to split these sections with an extra newline) then makes it simpler to call these by loading them into a constant via let. The next section loads the executable (/usr/sbin/netstat) and the next appends arguments to it (-R). Then, we run the information loaded into process with the type method run (https://developer.apple.com/documentation/foundation/process/2890108-run). The pipe (https://developer.apple.com/documentation/foundation/pipe) is necessary as it allows us to communicate between processes. The full netstat swift file is as follows:

#!/usr/bin/env swift

import Foundation

let process = Process()

let pipe = Pipe()

process.executableURL = URL(fileURLWithPath: "/usr/sbin/netstat")

process.arguments = ["-R"] try! process.run()

let data = pipe.fileHandleForReading.readDataToEndOfFile()

guard let standardOutput = String(data: data, encoding: .utf8)

else {

FileHandle.standardError.write(Data("Error in reading standard output data".utf8))

fatalError()

}

Now, anyone that runs the above file will notice why I chose netstat. The netstat will continue to run and display output to the screen, even after the swift script is stopped. We spawned a process but didn’t kill it. There’s always a better way than shelling out a zsh command in swift. However, while learning swift it provides a partial ramp for tasks people know how to do easier. After years with swift I’m just finally to the point where I refuse to invoke a bash command any more… Just keep in mind that sip or sandboxes can block certain tasks from completing.

Most of the people I know that use shell scripts or drop into the command line consistently aren’t patent attorneys; they’re people charged with automating tasks on vast numbers of computers. A common task for those who manage Macs is to read a property list into a variable (or a part of a property list) give that this is how preferences are stored on a Mac. That can be done via bash easily by using cat to display the raw contents of a file, defaults to walk through simple defaults domains within property lists, and plutil to walk and parse through more complicated property lists (e.g. those with nested arrays). Let’s take the property list off the table for a second and look at how we might read the contents of any file (although in this example it will be a property list) and then print it to the screen:

#!/usr/bin/env swift

import Foundation

let path = "/Library/Preferences/org.cups.printers.plist"

do {

let plist = try NSString(contentsOfFile: path, encoding: String.Encoding.ascii.rawValue)

plist.enumerateLines({ (line, stop) -> () in

print("\(line)") }) }

In the above script, define the path for the file (swap this with most any file not protected by SIP or a sandbox to simply produce its contents written to the screen. Then, we import the Foundation framework (https://developer.apple.com/documentation/foundation) to get at NSString (https://developer.apple.com/documentation/foundation/nsstring), which has options for reading the contentsOfFile (in this case the file is the path variable). Other parameters are included via dot notation. That’s loaded into the “let plist” but there’s one more issue, we have to do this line by line, so we’ll loop through those with enumerateLines (https://developer.apple.com/documentation/foundation/nsstring/1408459-enumeratelines). At this point the script still processes linearly like in old school procedural programming and the variables are lazily typed, so the structure is about as simple as we can make it. Still, with the all the curly braces, parenthesis, and extra steps (not to mention a little dot notation which looks nothing like the lambda calculus that inspired early programming languages), this script isn’t simple to read or write without some understanding of swift.

The most basic tasks of an admin include automating a task, finding or flipping a bit of information in those property lists (a task often best left to an MDM now, but check out https://medium.com/cracking-swift/parsing-remote-xml-using-swift-bfa9701fff84 for more on doing so within swift. Having said this, everyone is more savvy than they were when the shells were evolving – most especially admins with sprawling fleets of machines to manage!

The post Swift, Shells In The 1960s, And Some Swift Scripting Examples For Admins appeared first on krypted.



This post first appeared on Krypted.com | Tiny Deathstars Of Foulness, please read the originial post: here

Share the post

Swift, Shells In The 1960s, And Some Swift Scripting Examples For Admins

×

Subscribe to Krypted.com | Tiny Deathstars Of Foulness

Get updates delivered right to your inbox!

Thank you for your subscription

×