 Cool, it's the audio coming out fine Yeah Cool, so this is just a short overview of writing shell scripts in closure or to be more specific closure script I had some Data manipulation from Jason files and endpoints that I was doing and it was getting really hairy doing it with a Combination of jq and javascript and some shell tools and I thought well closure has a lot of great You know standard library functions for manipulating data So let me try using closure or closure script to write my shell scripts And then I started looking into this and I'll just share what I've learned and Some of the useful functionalities that I found for doing that So this is writing shell scripts in closure script using plank What is plank? Plank is another repel Similar to your lining in repel or your boot repel plank is this It's a closure script specific repel I think it runs on top of the Javascript core engine and It's available for at least Mac OS and Linux. I'm not sure if you can get it on Windows They don't say that so maybe not but it's very lightweight and For shell scripts the thing that is really important is it starts really fast so well, let's do a comparison if you start your line repel versus your plank repel So line repel Still taking time blank is ready Right, so that's the main difference for me in shell scripts You don't want to wait for the repel to start and to stop. You just want it to completely execute very quickly and Plank gives you that ability So being a closure script repel closure script traditionally lacks a lot of the tools for interacting with files and the shell and things like that Because it's targeted more at the browsers as the first first sort of target platform But plank includes a lot of these things. So I'll walk through what all those functions are in plank and how they can be used So the first one is Invoking external shell tools. So if you're writing a shell script and you want to You know invoke some shell tool that already exists. You can do that in plank using the sh function It's built into the plank shell namespace So it's just straightforward just the name of the command and the arguments and what it gives you back is This triple of the exit status of the command The standard output what I was printed there and whatever went to standard era One thing that keeps tripping me up when I don't use this for a while is Remember to separate the command and all the arguments as separate strings if you send it off as one string it's going to give you an error that Makes no sense and it's really hard to debug so the launch path not accessible But I spent like a lot of time Breaking my head over that error and googling it and stack overflowing it and then I realized that I just have to separate the command and the arguments and And just like any other closure thing once you've Executed this you can manipulate it however you like you can print it out or you know anything else Cool the second thing that's quite useful for shell scripts is Getting all the command line arguments that are passed into the shell script in plank They go into this global variable called command line args So I have this script here called args.clgs and all it does is it prints out the command line args So if I just execute that With these are some arguments it's just gonna print that right back out cool so far any questions The next one is writing and reading files So enclosure script slurp and spit that exist enclosure are not present by default But plank includes these in the plank core namespace So they work pretty much exactly how they work in Sorry, let me just stop this again. So they work pretty much exactly how they work in your regular closure stuff Once you've required it you can just use Slurp so I have this file. Let me show it to you It's just some lyrics from a song. So when you slurp that you get that as a string and Similarly, if you take some data you want to put it in a file You can spit that back out with plank and now you can see there is a math dot Text in here with the result So pretty regular stuff One nice feature that I like with slurp is you can give it a URI So if it's like maybe some JSON data or an endpoint or an API that you're accessing You don't have to shell out to curl every time if it's a get request You can just slurp it So this for instance will go to that URI and give back whatever response it is in this case It's a JSON response. We can go to that website and see Yeah, so it's just a regular JSON response and Plank will just kind of download that a remote endpoint for you and give you the result in a string JSON string The next one is getting data from standard input So usually when I write the shell scripts, I try to make them as Independent from the data they're reading as possible So I try to like pipe the data in rather than read from a specific file or something like that so in plank you can do that by Requiring the in special variable and the slurp method so you can essentially slurp the in Stream and that way you can read whatever standard input has So for example, we have this file standard stdn.cljs so I require slurp and in and Just in this case. It's like an echo. So just prints it back out So if you do something like Echo hello, and you send it off to plank stdn.cljs So it's just gonna print that back out Then the nice feature is since it's a closure script based repl Your JSON parsing and JSON serializing functions are already available. You don't need any external libraries for that You can so as an example What we'll do is again, I forgot to start plank So Require this so now I'm setting data to be this data from the remote endpoint So it's just a string of JSON text and then This is the JS interrupt syntax in in closure script So from the JSON global variable called the parse method Oops, sorry from the JSON global variable called the parse method on the data Variable and it'll give you this Passed version of that JSON string But if you notice here, you see this map here is actually tagged with this hash JS syntax This is actually still a JS Map and a JS array. It's not a closure map or a closure array or a closure vector so when you pass using the JSON parse you're still getting that Native sort of JS based map and array structure, which is not very nice for us because we want to be able to manipulate that using closures Functions and we want, you know to be able to use all the immutable data structures So the way you do that is this this is a closure script feature not a plank feature. There are these global methods JS to CLJ and there's another one CLJ to JS. So what this does is take that native JS object and map or basically an entire structure of that It can be as deeply nested as as as it is and if you pass it to that it'll then convert it into The closure equivalent. So now it's a closure data structure and you can see there's no hash JS and stuff anymore One thing that we have lost is The previously when we passed it the the keys in the map were keywords Now they've changed into strings The keywords are still pretty useful because keywords in jail in closure functions and you can use them to access the maps so If you want to keep them as keywords, just remember to pass in the extra option keywordized keys as true and then you'll get the same basically your closure version of the JS maps and Objects cool so far All right. So the second part of Jason. So that was the parsing part. This is the serialization or the stringifying part So CLJ to JS will take a closure map convert it to a JS map It basically just looks like the same thing with hash JS in front of it You can manually construct these literals by hand if you want you could do hash JS One two three four and I've got that JS literal but most of the time It's preferable to just work with the closure data structures and then at the end convert them back out to JS It's a similar to before instead of dot bars this time we call dot string if I and it takes the closure map converts it to a JS map and then string if serializes it using JS Using the Jason string if I method if you do it directly without the CLJ to JS transformation then you get a whole bunch of crap and most of the time I think we don't want that so Remember if you're seeing that whole bunch of crap, it's probably because that CLJ to JS has been forgotten One thing that people don't generally recommend But I find is okay to do in shell scripts is you can do a shorthand version of the parse and string if I So instead of doing dot bars JS slash Jason You can directly use Jason dot bars and Jason dot string if I the reason it's not recommended is if you or some other Library overrides the Jason global then it's not gonna it's not gonna inform you that something's gone wrong Whereas the other method does but mostly my shell scripts have been at least the ones I've been writing have been pretty small And I know the dependencies they have so that sort of succinctness is just is worth it in this case But yeah, it works basically the same way So it'll give you the same results One thing I found myself making quite heavy use of is the threading macros in closure I find basically when you're writing shell scripts and you know piping your data through various tools The threading macro kind of emulates that so you take a bunch of data in this case a URL pipe it through slurp and then pass it with Jason parse and then Pass it to JS to CLJ Q what I think the keys so it inverts that nested function call and kind of flattens it out and for When you have long bunch of Transformation functions, it makes it very easy to see all the steps in the transformations You can see okay now. I'm getting the key now. I'm you know reducing it to some other value now. I'm manipulating it some way So instead of this nested it's so this and this are basically the same But it's I find when it gets very nested it's much easier to read the threading macro version me Yeah, so This one and this one Will give the same result Has anyone used the threading macro before yeah? So if you're not familiar with it what it does is whatever you put as the first argument This one it's going to then take that result and pass it as the first argument to the next function It's going to manipulate that with that function and then pass it as the first argument to the next function And this is where I also find the json dot parse is a bit nicer because if you're using dot parse JS slash Jason Then Threading macro will actually try to hang on yeah, the threading macro will actually try to place it somewhere here and That's going to mess up the flow. You have to wrap it in a in an anonymous function or use the other threading macro The thread last instead of thread first Cool, so that kind of wraps it up. I have an example script that I can show you So I'll walk through what this does So basically I just kind of it's a very contrived example, but I have here a file called albums.json it's Jason a file which has an array and each element of the array is an object with an artist and an album and What this script will try to do is for every item it So basically tries to count all the number of albums each artist has and then sort it in descending order so the Yeah, so the artist with the most albums is going to be on top and then the next one and the next one So we start with this she bang line, so it tells the script to be invoked with the plank interpreter Then I require slurp and in and Here I've kind of separated the input parsing section So getting the input json converting it to closure structure and storing it in a variable called input and the reason is that Well part of the reason is What I want to do in the output requires the thread last macro and what I want to do in the input requires the thread first macro And I've separated those things out The other option is there's another version of the threading macro called the as threading macro And I'll show you what it looks like. This is the same file, but using the as threading macro And what that does is? It still lets you pipe your data through that pipeline of functions But now you give it a name you give it a sort of placeholder symbol. So you say my input is called D after I slurp This D argument whatever that result is now that's going to be called D And then after json parsing it whatever I get from that that's going to be called D so now I can kind of Whether that D is the first argument of the next function or the last argument or whatever argument doesn't matter You can just put it in the right place and you still get this pipeline of functions it Has a few more parenthesis than the other version, but I think Like potato potato it has its benefits as well So here what's happening is you read the standard input parse it Converted to a closure or data structure and then to the output you get all the artists from each element of that array Frequencies is a closure very neat function that basically takes in It's built into the standard library takes in a vector and for every item in that vector It'll tell you how many times it appears So it returns your map. So here it says the number one appears once two appears once Three appears twice four appears once and so on So if you have an array of strings for instance It'll tell you cat appears twice dog appears once So I'm just piping that list of artists to frequencies So it counts how many times it appears and then I'm sorting it by the second element because in frequencies The first element is the thing itself. The second element is the number. I'm sorting those numbers in descending order and Then finally for every item in the output. I just print out the frequency and the name So all put together it looks like this So I can pipe it to Sample that CLJS And that gives I realized after contriving this example that it's fairly easy to do this with normal shell tools as well you can basically Sort of parse the Jason by hand by cutting on the colons and the and the double quotes but yeah, it's it's more fun to do in closure script and And for the things that I initially tried writing these in closure script Those are quite quite involved with a lot of group groupings of things happening a lot of merging of maps happening Which closure was quite useful for? But yeah, yep, that's basically it. I think I don't have Yeah, that's all any questions The editor is Vim the terminal is just using Tmax and Vim has a plug-in that like basically lets it Kind of shell out to Tmax and execute stuff in Tmax So here I can just say so I can type it in Vim and then it'll execute it in the pain below and Can zoom the pain then stuff like that? It's called Vimax Yeah, yeah The presentation is actually just a closure script file if you see here and If I zoom it out, you'll see it's just a really long file with a lot of spaces in between Yeah, I made it pretty manually and the what I did was If you notice every slide starts with this comma Followed by the the title of the slide, right? So I have a little function. I have a little mapping here in my VMRC So basically it Looks for two line breaks followed by a semicolon and a space and it jumps to that So it jumps to the top of that so when I press that key binding it goes to the next slide the next slide Yeah, so it's it. Yeah, no not much is actually happening here Oh The fonts I actually found this program while I was looking at something else called toilet so if you take a piece of text and Send it to this program called toilet It'll format it nicely for you and you can pass like So they have some pre-built fonts and things like that So that's that's how I got that Toilet and then you can say some there's some arguments for colors and stuff like that Yeah That's about it Cool anything else All right. Thank you for your time. I'm feel free to ask me later