Transcript
[0:00] Music.
[0:08] Well, it's that time of the week again. It's time for Chit Chat Across the Pond. This is episode number 762 for March 18th, 2023. And I'm your host, Alison Sheridan. This week our guest is Bart Bouchard. It's back with Programming by Stealth, number 146. What are we going to learn today, Bart? Sorry, 147.
Yeah, because my show notes, until I corrected mine a second ago, said PBS 146 of 7.
I like it. I like it. I still had 146 up and 147 next to it. So if anybody's wondering, programming by Stealth147.
And if anyone enjoys snooker, that is the maximum possible break you can have in snooker.
Ah, well that's what we really needed to know.
Yes, absolutely. Anyway, that number just stuck in my head for some reason. So I promised you a raise and I promised you associative a raise and I'm going to have to break one of my promises and I'm very cranky about it. I ended up throwing out about an hour's worth of show notes and being a grumpy bunny earlier today. But anyway, we're going to have a good show regardless because I think I've managed to rescue it. But I will explain why when, we get that far.
So today is a raise. All about a raise. But before we get to do new stuff, we get to have a look at our challenge from last time. I get the impression from the PBS slack at podv.com forward to our Slack, that lots of people had lots of fun with this challenge.
[1:37] Yeah, we did. Several of us have been chatting and, oh shoot, you put me on the spot here to remember names. Somebody started it off. One of the Eds.
Was it one of the Eds? Shoot, I'm going to look it up really quick here. Not only talked about the challenge, but said, hey, I've put mine up on GitHub. Do you guys want to, anybody want to look at it. So then I put mine up on GitHub and started playing back and forth and started learning from each other. Was it Ed? Shoot. At some point I'm just going to yell out who it was because I'm having to scroll back and find it. But it's whatever Everyone has a lot of comments, but yeah, it looks like there was a lot of fun with this one.
Yeah, it was a big thread, which is always a good sign. So the challenge was to write a script that accepts a whole number as an input, either as the first argument or from a user prompt, then prints out the standard n times multiplication table to the screen.
[2:36] And then there was also the stipulation to use the bc terminal command, which is the basic calculator, to do the math. And finally, for optional credit, you could add support for a second argument to specify the maximum number the table should go to. So instead of being 1 to 10, it could go to a different number. So you will find my challenge solution in the zip file for this installment. In fact, it's the only thing in the zip file for this installment. And it's in the show notes.
So...
Eoin Lessing started it. Oh.
And he and I jumped back and forth a whole bunch, and then who else jumped in? Shoot.
I know Ben was interested in playing as well.
[3:21] That's the other person that was playing with us. Excellent.
Which is great to see.
So for the most part, I think my solution is pretty by the book.
I was trying to find something to pull out of it to draw attention to, but for the most part it's just, it's the very same building blocks we were using in all of the examples.
So I used an until loop to keep asking people for input if they needed to.
I used plenty of conditionals with, I think the most exciting thing was a not minus z to look for things that are not empty, to see if argument two was not empty.
Let me see, I have a standard for loop.
I use the sequence command to make my sequence. But I guess the only thing I did want to draw attention to was that I had never used the BC command before.
I just did a really, really, really quick Google when writing the show notes last time to make sure it was possible to do math in some sort of easy way.
And the answer was, yes, it is with the BC command. And so I was like, fine, that was a challenge.
So I had to go learn it myself.
And I discovered two things.
The man page is terrible. It makes this command seem overly complicated.
But Googling for the answer made it really, really easy.
Just pipe a string with the equation you want into BC, and it will give you the answer.
So if you just type echo the string 1 plus 1 and pipe it into BC, it'll give you two.
[4:50] Oh, that's easy. So that allowed me to just echo, you know, double quote, dollar n star dollar num, and then pipe that to BC. And that allowed me to save my product into a variable I called prod. So that turned out to be easy.
I'm curious what you used a SEQ sequence for. Did you teach us about sequence? I'm drawing a blank.
Did. So you can make sequences either if you know the full range, you can use the dot dot syntax or you can use the seq command. It was literally the, pretty much the last thing we covered in 1.46 was sequences.
Okay. I used a while loop to do that. Also fine. Like I said, there's an infinity of correct answers to all of these things.
I was lazy, so I used a for in loop with a sequence. But it has exactly the same effect, I'm going from one to 10.
However you get from one to 10 is entirely up to you. No, I don't always pay attention. Oh, go ahead.
[5:54] I was going to say, I was about to start something new, so I'll let you go first.
Okay, so I'm not always good at reading instructions, so I actually did more than I was supposed to, but that turned out to be really fun.
I didn't just set it to how far above one it would go. I said, you get to define both.
So you can say I want the multiplication tables from 26 to 2428.
And then as I was looking at whole numbers and such, I thought, well, what if I want to use a negative number?
Or what if I want to use, what if I'm not good at reading instructions and I give you the maximum is smaller than the minimum?
I decided to let them have the table counting down instead of counting up.
So I gave them a lot of freedom on that.
And you can use a multiplier of a negative number with mine.
And I just kept having fun. So what else could I have them do?
It was really a gas. I enjoyed it.
I am so happy to hear that because the idea of the challenges is not that everyone does exactly the challenge. It's just that I help you get over the empty page syndrome.
[6:59] And so if you run with it, that's a feature, not a bug. So that's fantastic.
Another small thing to draw attention to in my solution is a little trick I use very, very often when I want to make sure I'm getting a greater than zero whole number, and I don't want to do any math, you can cheat that with a regular expression, which is, starts with 1 to 9, followed by 0 or more 0 to 9s.
That means it will be 1 up. So I use that a lot in my solution.
Yeah, I just put a square bracket. It's a little cheat I'm fond of.
I did the same thing. I did a square bracket, starts with plus or minus, and then a question mark 0 to 9, with a plus at the end of it.
[7:45] Okay, so you're allowing negative numbers. Right, but 0 to 9 with a plus after it does the same thing as what you said, right?
It can be any number of 0s to 9s.
Right, but I don't want 0.
I want a greater... Well, because it doesn't make sense to go from one to zero.
Because my table was going from one to a number, so I wanted a number from one.
I've always wanted a greater than zero whole number, which is why, you know, it starts with one to nine followed by zero or more zero to nines is the little trick.
It means you get a number greater than one without having to do math.
[8:23] I also put in some stuff to say, hey, do you want to define the min and max?
I ask them if they want to.
And if you hit enter, it means no. If you hit Y or Yes, or capital Yes, then you get to define it.
So I said, yes, type Yes, but I know people would type a Y, so I just let them do it.
That was fun. But then using the ternary operator...
Sorry?
[8:49] I was curious how you decided to implement the matching of Yes or Y.
A common approach is to use a regular expression starts with Y and starts with N and basically ignore the rest of the string and do it as a case-insensitive regular expression.
So if they type yellow, they would get yes? Yeah. Yeah, it's close enough, right?
No, so I played with the double pipes for or, and I don't know if I'm saying this exactly correctly, but it had to say if square bracket quote dollar yes no, which was my variable I was looking for, did you say yes or no?
So if square bracket dollar yes no inside quotes equals quote yes.
And without the quotes around the variable name, it said, I found errors on that and it said there was something about POSIX compatible stuff and so by putting those quotes it seemed to work.
And I lost track of which way was POSIX or not POSIX, what I was doing.
Well were you using single square brackets at any point? Double.
[9:58] I know it was, it said without the quotes, if you're doing it with single brackets, if the value doesn't exist, the variable vanishes.
So it becomes single square bracket equals yes. It loses the variable itself if you don't put it inside quotes when it's single brackets.
That's what I did. And yes, and so single brackets were what we said not to use because it's the legacy old SH stuff and therefore use your double square brackets so you have modern posix and your life would be easier.
Yeah, there was a reason. Yeah, I played around with that too. I ended up with a single bracket so I did it backwards, but I did do it both ways.
Well, I guess one thing just I would suggest that you get into the habit of always using double just because it will make your life easier.
I'm going to run it again right now just to just to enjoy myself with it with the double square brackets. And no quotes.
[10:57] Now, I'm going to use this as a teaser point for something we're about to come to soon.
So having the user enter in stuff isn't really the normal way that terminal commands work, right? If you have an optional second parameter in the terminal, the way that's normally implemented it was a named argument. So maybe like table.sh5-m11 for max11 or something. So those minuses, that's terminal, excuse me, can't speak. That's normal on the terminal, but we don't know how to do that. But it's everywhere in the terminal, so it must be easy, right? There must be a tool for that. And the answer is yes, there is. It's called getopt, and we're going to meet it next installment.
I'm lost. You just said minuses aren't there. What do you mean?
Right. So your script should be able to work like every other terminal command, and if you have something optional, you should be able to have it do a minus m for max, or minus capital M for max and minus lowercase m for min.
I don't know what those terms... Where did you come up with minus M, minus capital M?
I made it up. I am saying terminal commands use minus and then some letter to take optional arguments. Our scripts should be able to do the same. We should be able to write our script so it looks like a terminal command, so it behaves like a terminal command, so it supports minus whatever we choose to make it support.
[12:25] Okay, I don't see the value of that.
[12:32] What's not good about the way it is now, about what we've written?
It's nothing like terminal commands, so it's going to confuse people.
It's out of step with the norm of the terminal, so you should be writing your shells...
I would make the argument that good shell scripts that won't confuse users are ones that do things the shell script way.
So you mean when it says, give me the minimum value by which you want to multiply, I should instead force them to say minus m and type a number?
That would be the terminal way, yeah.
Okay. That seems a lot less human-friendly to me. Well, no, because if you're making people type input, it's not just not in the style, it also makes your script un-automatable. amazable.
[13:16] Because you can't use stuff that prompts on the terminal inside automation.
So now that you're in your big buzz about automations, your shell script now can't become part of an automation.
Your shell script also can't become part of a sequence of piped stuff.
Right. Because the whole joy of the terminal, I always think of temp reporting.
Right. Each terminal command does one thing and does it well and you connect them together with the pipe.
[13:40] But if you're expecting people to start typing stuff, then you can't use the pipe to flow the input through, Because the standard in is either the keyboard or the pipe, as we'll learn in the next installment.
Okay, I'm catching up to you then. But so right now, the way it's written, you call the shell script and you can put three values after it, which would give it the min, the max, and the number you want to multiply by with MyScript.
But it doesn't say dash, lowercase m, or dash, capital M. You know, there's no way to tell it which one you mean.
It's just the order that they're in. in, so they're like $1, $2, $3, which works, but you're right. I see what you're saying.
Well, that's okay for two or three possible arguments, but for four arguments it becomes a bit, ooh, and for five arguments it's really ridiculous.
Which ones which? Yeah. Yeah. Right?
[14:32] Okay. Okay. I caught up to you. Yeah. So basically what I'm saying is we're going to make our script work like other terminal commands using the wonderful getopt command. Then we can have our little minus signs. That's It's just a little teaser.
So today's actual lesson is all about arrays or lists in Bash.
All of the little snippets as we go through the text are designed to be copied and pasted into a Bash terminal.
And if you paste them all in, they will all work. So they all build on each other.
And they're just illustrative examples as well. So they're doing double duty here.
So you can play along, or you can just look at the commands. the way.
[15:11] Up to the listener slash viewer slash reader slash whatever.
[15:17] So many ways to consume our content. Okay, so step one, to make an array exist, we can create an empty array using the declare command with the minus a flag for array. So we can say declare space minus a space dessert list and that will make an array called dessert list that is empty. We can then add values to that array by appending to that array with the plus equals operator. So we can say dessertList plus equals waffles. And we now have an empty array that isn't empty anymore, it now has one value, waffles. We can also use the square bracket syntax we're very familiar with from JavaScript. So we can say dessertList1 equals pancakes. Note that they are zero indexed, so dessert list one would be the second element, so our array is now waffles followed by pancakes. And if we want to get values out of an array, we have to use the dollar sign to pull a value out of a variable, but we need to use the dollar sign with curly braces so that the dollar sign applies to dessert list open square bracket zero close square bracket. You're going to say I want the value of all of that.
So you say dollar open curly name of your array square bracket your index close square bracket close the curlies. So you basically have to wrap the dollar around the whole thing is how I mentally remember it.
[16:46] So that makes sense. That's a lot of... I'm starting to get real tangled on square brackets, double square brackets, squirrely braces, quotes, dollars.
And I'm going to make it worse. So well, let's not yet until I finish stressing on this one.
So we say dessert list square bracket at, and apparently at says give me all the contents.
Whoa, whoa, whoa, sorry, you've skipped ahead. Did I jump ahead? You've jumped ahead.
Sorry. Okay. Echo, well, okay, now I don't know what I'm reading.
So we've put waffles into zero and pancakes into one.
So you said dessert list square bracket zero, so that should be talking to waffles, the first thing we put in.
[17:33] But since it's an array, you've got to use square brackets around the zero, so at this point we don't want to use square brackets, we go with squirrely brackets, because that, makes it less confusing?
No. Okay, so the difference... Okay, so when we're assigning to a variable, whether it be a plain variable or anything else, you don't use a dollar sign, right?
It's name of variable equals value. So sticking pancakes into the array was just dessert list, open square bracket, one, close square bracket, equals pancakes.
Pulling values out of a variable, you have to use the dollar symbol.
Right. I'm not talking about the dollar yet. I wasn't there. What do the squirrelly brackets mean?
That's how we pull things out.
No we pull things out with the echo beforehand. No the echo just prints what we have.
Okay. Right? So you pull things out by putting squirrely brackets around the array. No, step back.
So how do we get, if I have a variable called boopity, if I have a variable called pink, how do I get the value of pink?
[18:42] I'm waiting for you to tell me. I don't understand what you're saying. $PINK.
OK, if I have a variable called X... Sure, $PINK. Right, but I'm asking about the squirrelly brackets, not the dollar.
Right, OK. I know the dollar is asking for the variable. I know that.
Right. If you say $DESSERT, open square bracket, you will get $DESSERT, which is undefined, and then you will get appended to the string, open square bracket, zero, close square bracket.
The dollar has to be told... You're not applying to the word, you're applying to this bigger thing. The dollar has to be told to stretch its application.
[19:16] Okay, so they could have done it with quotes. They could have done it with dashes. They could have done it with anything, but they chose squirrelly brackets to say, when you've got dollar and then you start a squirrelly bracket, look for the other closing thing. And I mean, find what's inside all of this.
Right. And they couldn't use the other symbol because they already have other meanings. So so they needed to find something that doesn't have a dual meaning, right?
Because you can't give a computer a mixed message. Your computer has to have a unique meaning for every symbol in every context.
So the curly braces are there to stretch the dollar symbol.
To wrap it around the full name of variable. After you've got the dollar squirrelly bracket, close squirrelly bracket, you got to put it in quotes.
Well, that's because we want to use it as the input to echo, right?
If I want to echo any string with a space in it, I have to quote it.
The echo command wants one argument. My desert list could have a space in it.
It doesn't in this case. I could have said popspace tarts.
Then I wouldn't need...
So the quoting is there like it would be in any other variable.
Okay. I'm glad I dragged you through explaining it slowly.
No, it's important. It's very important. I was hoping you would. Remember, that's your job here.
Making me do this, right? It's very valuable and I really appreciate it.
You always say that, but I always feel like such a pest. But okay.
No! See, to the audience, he gives me permission to do this.
[20:42] Yes. Not just permission, I ask. Okay.
So that gets you a specific element in the array. But you often actually want the whole thing.
In fact, one of the coolest things in Bash is that you can turn an array into a list of arguments.
And you do that by using the special array index at symbol. So when you say name of array open square bracket at close square bracket, that means give me every element in the array as a list of arguments.
And so you can just shove that as a single argument on the end of a command and it will explode into all the elements of the array as each one a separate argument.
So if you have an array with four elements and you use this syntax, that becomes four arguments to your command.
[21:32] Oh, okay. Okay, because it's just strings separated by spaces.
Yeah, which is why the terminal works, right? And that means you can use this in...
Ooh, there's a strange... Sorry, I have a very, very silly typo here.
So for dessert in, you don't, there's no echo there, for dessert in $dessertList, so you can use this as the, you can use an array as the input to your loop, right? Because a loop expects you to have a list of arguments to loop over. Well, an array can be exploded into a list of arguments with the at symbol. So we can loop over our desserts by saying for dessert in $open open squarely dessert list, open square bracket at, close square bracket, closer curly.
Why isn't it in quotes, Bart? It came after an echo. The echo is a typo.
The echo needs to disappear.
Oh, oh, that's what you were just saying. Okay, so for dessert in, and we don't need to...
Okay, so for dessert in, dollar, squarely bracket, dessert list, square bracket at. Okay.
And that will explode today. — We'll explode it out. — These two arguments will pop— — Yeah. — Or these two values in the array will pop out as separate strings.
And then what are you going to do with it?
[22:50] — Well, they're now coming out— they're now for dessert in— it's effectively as if we had tied for dessert in pancakes, waffles, or waffles, pancakes, sorry.
So our loop will happen twice, right? So the array has become the values to loop over, because we've exploded it out.
— Okay.
It's really cool. You will do that all the time. You'll just explode arrays out with the square bracket at close square bracket.
It's not short syntax.
It's not obvious syntax. but it is powerful.
In fact, it's very confusing, if I'm completely honest, but you do get used to it. Thank you.
[23:30] So you can also create an array without this whole declare minus a and then plus equals and all that stuff. You can just take a list and make it an array, right? We just turn an array into a list where you can go the other way by using round brackets. So you can make an array in one go by saying name of array equals open a round bracket and then as separate arguments, all of your values close round bracket. So because it's a list of arguments, your space symbol is treated as the separator. So if you want to have a space, you either have to escape the space or quote the string.
Okay, for example, you did both.
I did both to prove the point. So breakfast list becomes equal to, or breakfast list equal, and round bracket, porridge space pop backslash space tarts space single quote French space omelette close single quote will make a three item array. The first item is porridge. The second item is pop space tarts. And the third item is French space omelette.
Okay, I like seeing all those examples together. Does it matter, single or double quotes?
In this case, if I needed to have the value of a variable inside my string, then I would need to use double quotes so that I could have $nameofvariable.
Okay, but FrenchOmelette could have been in double quotes?
[24:53] Sure. There's no dollar symbol in there, so the double quotes would be telling Bash to look for variable names to expand and giving it none to do. So it's mildly more inefficient.
[25:05] Okay, but it would recognize FrenchSpaceOmelette as the value for the third spot in the array.
It would, yes. So double quote means interpolated string, so process the string looking for the dollar symbol.
Okay, okay.
And single quote means, I'm just giving you a string. Don't even try to interpret it. It's just a list of characters.
So the double quoted you said is interpreted?
What? Interpolated? The actual jargon is interpolated, which is not a word that means things to human beings.
That's a computer science word. Well, it's a mathematician word too, but is interpolated what? Interpolated quotes?
So interpolated means that it's... the content of the double quotes is checked for special characters like the dollar sign.
And then those characters get interpreted as if they are what they are, which is commands.
[25:58] So if you say double quote name of a variable with a dollar sign, then the value of the variable goes into the string.
If you use single quotes, the dollar character followed by the name of the variable will be in the string.
Okay, okay. I gotta make a note of this because I'm never going to get that straight the next time. So, just a string is the single quotes, and double quotes will deal with a string, but it's really used for looking for a variable name with a dollar sign.
Yeah. Okay. It's like a smart string. It's like more of a string, hence it's two quotes. That's how my brain remembers it.
More of a string. Okay, I like it. I like it. Okay, so, so far I have already thrown a sea of symbols at you.
This syntax is not obvious. There is one more symbol I can throw at this.
How long is an array?
How do I get the length? You get the length by using the count operator, which is the octothorpe symbol.
And you shove the Octothorpe symbol after the opening curly brace.
[27:11] And then you use it in conjunction with the at symbol. So to find out how many items are on our breakfast list, we have $openCurlyBrace, octothorpe, breakfastList, openSquareBrace, at, closeSquareBrace, close the curly.
That will tell us how many things are in the array breakfastList.
That's exceedingly annoying syntax. Yes, it is.
I'm not against the octothorpe because it's often called the number symbol, right?
So the number of, the length of, I can live with that.
But putting it inside the squirrelly braces, but I guess, okay, let's see if I can get it.
So breakfast list, square bracket at, means everything in breakfast list.
We're going to put a number in front of that that says, I'm not talking about the breakfast, list itself, I want to know how many things there are in it.
[28:02] But then since it's a big long thing that's going to be interpreted as a variable, we've, got to put squirrelly braces around the whole thing and the dollar sign beforehand to make it a variable.
All right. Yeah. That is the logic. You've perfectly articulated how Bash is working under the hood.
That is how the Bash compiler is looking at that wall of text.
So you've perfectly explained why it is what it is. As a little bonus, though, if you want to get the length of a string, you can actually use the same syntax.
We just haven't mentioned it before. So if you want the length of the first element in the array, if you replace that at with a 0, you get the number of characters in pancakes. Or waffles, whatever one is first. I can't remember where we're at. I think I did waffles first. So if you want to see the length of the word waffles you can replace the ash with a zero.
[28:49] Which is mildly useful. And actually it will work with any variable. So if you have a variable named cat, so in the example here we have cat equals Felix, then you can echo inside double quotes, there are dollar open curly octothorpe cat close curly letters in dollar cat. And that will print out there are five letters in Felix.
[29:13] Oh shoot, I wanted it to be three. You're right though, that would be five. Okay.
And that is what your double quote is doing, right? Your double quote is basically saying all of those $this and $that, do all the work and turn them into five and Felix. If you, run that same echo command with a single quote, you will see the difference.
Wait a minute. So if you said echo single quote, there are $... Whatever.
Octothorpe cat, you're saying it wouldn't work?
Well, it will work. They wouldn't know what that variable was in there?
It will do what it's supposed to do. It will make the string, but it will literally come out as the character $, the character open curly.
Okay. Okay, because it's saying, you gave me a string because you put it in single quotes.
I'm going to echo that string right back to you, so it's not going to interpret it.
I'm sorry, it's not going to interpolate it. Yep.
That is the jargon. That is the jargon. So if you're Googling for it, that is the jargon.
You have to know whether you agree with it or not. That is what it is.
And then one last very useful thing. So because the at sign explodes your list into individual pieces, and because you can make an array out of individual pieces, you can use that to create arrays from arrays.
[30:37] So if you want to make a brunch list that contains everything that's for breakfast and also has some burgers and some fries you can say brunchList becomes equal to open the round bracket, a started new array and then inside double quotes, for reasons we'll get to in a moment, I haven't forgotten, and I did it very intentionally inside double quotes $openCurlyBreakfastList, open square bracket, add, close square bracket, closer curly space burger, space fries, we get a new array which will contain French omelette, what did we say, Pop-Tarts, and whatever the hell else was in that array of breakfast list items.
Where did I make that array?
Anyway, it will contain... there were three things in there already, now it will contain five.
Okay, so let me say this again. So you said brunch list equals, and you did round brackets, because that's how we can... one of the ways we can create an array.
And we're going to put three things in that are space delimited.
The last two are easy, burger and fries, with just spaces between them.
But the first argument, you're calling the array that we already created, which was breakfastList() at.
[31:41] But we wrap it in squirrelly braces so that the dollar symbol knows what to consider the variable.
And then you've put it in double quotes because...
DG If I didn't, I would end up with a seven-element array, because French omelette would become French and omelette.
So the double quote says, treat everything inside me as being individual items.
[32:07] So the quotes kind of get magically multiplied around each element in the array.
Okay, so again, it's interpolating what's in there. It's not just taking the, string values that are inside it, because it would have that space in French omelette.
[32:23] Yeah, exactly. And in Pop-Tarts as well. Be smart.
Yeah, exactly. So it takes a bit of working out to make sure you have it right, but you can use the pound symbol to make sure you've made your array correctly. And if you count them, it will will come out as five, it does come out as five, I checked while writing the show notes to make absolutely sure. And then I left out the quotation marks and I did it again and it came out as seven, proving that I understand.
Okay, that's a good idea to use that to say, am I doing this right without having to go through and figure every step of it out? You can tell if it's wrong.
Yeah, right, exactly. I thought I was going to get five and I got seven. Huh. Or I wanted five and I got five. Yay. Take your pick.
So that is all of the pieces of arrays. So we can make them, we can add stuff to them, we can read stuff out of them, we can get the whole array as a nice list of arguments, which is really useful for reusing the array, and we can combine multiple arrays into a single array to build up bigger arrays, which is actually way easier here than it is in JavaScript.
So that is all of the pieces of using arrays.
And at this point I was hoping to tell you about the power of dictionaries in bash, or associative arrays as the syntax is called. Which would basically be exactly the same but instead of zeros and ones we could have like donkey and horse as the indexes.
[33:46] But, alas, although they were added to bash in version 4, which was released in 2009, We cannot have them on the Mac.
They also exist in ZSH.
But because they were so late arriving in Bash, ZSH implemented them its own way.
So they are one of the few things where Bash 4 code does not work in ZSH.
Almost everything you write in Bash works in ZSH, apart from associative arrays.
So as well as adding new features to Bash 4 that didn't exist in Bash 3, including these associative arrays, the other thing that changed between Bash 3 and Bash 4 is the license.
Bash 1, 2 and 3 are GPL 2.
Or GPL 1. Anyway, not GPL 3. Bash 4 is GPL 3.
And Apple have a philosophical problem with GPL 3, and they are not alone in that it is a controversial license. Which is why Apple have held back Bash to version 3 on the Mac, even though version 4 has been out for a decade.
And a bit. Can you install Bash 4 on the Mac?
[34:58] Three guesses how. Do you want to just have a guess which of our friendly, friendly tools would do that for us? I don't know, like Xcode or something awful.
Homebrew. Homebrew. Homebrew? Okay.
Brew, install, bash will give you the latest bash. Okay.
So you can bring your Mac up to the latest version. There are also associative arrays in ZSH. So you could, you choose to use ZSH for your scripts. But if you want to have a portable script that works everywhere, then if you use ZSH, you've cut the Windows people out and a whole bunch of Linux distros who don't ship with ZSH by default.
If you use bash four, you've cut the Mac people out and as you make them install something nonstandard.
So either you make someone install something.
Or your script isn't going to work for some people. Or you avoid using associative arrays.
They're all horrible options.
[35:50] Now, I went and sort of did a bit of mental math. I was like, do we actually need associative arrays for what we're trying to do here?
Which remember is to port my library and we're doing this because we're going to be writing build scripts. Well we don't need associative arrays for the build scripts. So I'm just going to not do them because if I do them in bash it won't work on the Mac. If I do them in ZSH, Well, we're using bash for XKPathWD.
So basically, they exist, the documentation is out there. If you're going to do them, know that they're not portable.
So you writing scripts for you, no problem at all. Use ZSH or whatever you like.
But you writing scripts for everyone, if you choose to use associative arrays, you have to redefine everyone to exclude someone.
So how important are associative arrays? Well, they weren't added until BASH 4.
They weren't added until Bash 4, so literally decades of use of Bash.
That was 14 years ago. Right, but that was 14 years ago.
Right, but Bash is from the 70s.
So Bash had three decades of life without them.
[37:00] Yeah, but we had a lot of calculus and things didn't work out so well.
I mean, there was a lot more we could do when we had calculus.
As I recall, we used dictionaries quite a bit in JavaScript, and they seemed fairly important.
Yes, but in JavaScript, you tend to be solving bigger problems than you're solving in shell scripts. Okay.
All right. You know, the difference between a programming language and a scripting language.
So on the whole, I actually don't think it's a big deal. It just made me really cranky because I use associative arrays from time to time with my work hat on, but in work we use Red Hat Enterprise Linux, which means we have Bash 4 and I hadn't realized what was missing from my Mac.
Your scripts that you're using at work only need to work on Red Hat Enterprise Linux 4? Yeah. Okay.
They're portable across all of our servers, which are Red Hat 6, 7, and 8, and 9. Not so much 6 anymore because that's now obsolete. But they used to work on 6, and now they're... So they've been portable to me in my little island universe, they've been completely portable. And I just assumed bash is bash is bash. And then on the Mac, I got very badly bitten when I pasted in the syntax I'd be so used to using and it just went.
You're talking rubbish. What do you mean I'm talking rubbish? And then I did a bit of googling and then it exploded with lots of cranky people all over the internet. Ah, oh dear.
[38:26] So now luckily you start the show notes three weeks in advance and you learned this like 18 days ago, right? That's when you found out, right?
Oh, 18 hours at most. Oh no, today, just after lunch. You know, I talked to people who who like get podcasts in the can weeks ahead of time.
Nah, we're just in time delivery.
Jit. Yeah, absolutely. You know, the teacher is like an hour ahead of the students sometimes, maybe two hours.
Well, it's very fresh.
So yeah, I didn't quite go to plan today. But anyway, I still think, trust me, the arrays are really important.
And I'm going to try to prove it to you by giving you another challenge, which I didn't dream up until I went cycling.
And one thing that was niggling me last time in the challenge is I didn't get to find a way to make you use the select loop.
So I've backported that into this challenge so that we can play with more things we learned previously in the series because it's all important.
So I'd like you to write a script to take a user's breakfast order.
So your script should store the menu in an array.
[39:38] And then it should use a SELECT loop to offer the menu to the users along with an extra item not in the original array, which is I'm done.
And every time the user chooses an item, I want you to append it to a second array representing the order. Until the user says I'm done. And then I would like you to loop through your assembled order and print it out.
I'm going to make you blah, blah, blah, blah, blah, blah, and print it back out.
So lots of looping, lots of arrays, right?
Can I let them add bacon when bacon-only Canadian ham is on the menu?
[40:15] Well, you're writing the menu, so your choice. If you want, I'd say go with...
No, I'm saying they can add to the menu?
No, that would be bonus credit. No, so they can add to their order, not to the menu.
So the menu will show them a list of what's available, and they'll click one for bacon, for eggs and that will get onto the array of their order. And so their order will end up being, you know, bacon, eggs, coffee, a muffin, I don't know, whatever you like for breakfast yourself. And then at the end, I'd like you to print out their order. If you'd, like some bonus credit, then I would like you to, rather than defining the menu in the script. I'd like you to read the menu from a text file. One item per line.
And ignore empty lines and lines starting with the OctoSwap.
Because if I was thinking about it, that's much more maintainable to have my menu as a text file and my script as a separate file instead of hard coding one in the other. There's your bonus.
And also it gives us an excuse to remember how to use the read command in conjunction with cat to pull things out of a file, which we did mention.
But again, I didn't squeeze it into the previous challenge.
So I'm I'm trying to backport as much as possible into the challenge.
No, I like this. I like this because you did teach us how to ignore empty lines.
So I remember that that was in there somewhere.
I don't remember how to do it, but I'm pretty sure I could figure it out. That sounds fun.
[41:38] I don't know why these little... I like these little challenges with small scope, that exercise just what we've just learned the best.
When they start getting complex and pulling things that we learned three years ago or whatever, that's when I kind of go, ah, it's too hard.
That the little ones get me practicing this one thing that we've learned, or this set of things. So that sounds really fun. I'm totally doing it.
Excellent. And there's a lot of scope here for you to improvise and go do many cool things. This is very expandable.
[42:09] Yeah. So if you are having fun doing this, go over to our Slack at podfeet.com slash slack.
Join the PBS channel if you haven't already. And I'm sure you'll find a discussion. If nothing else, Ian and I and Ben will probably be in there playing. And Ben asked a good question. He She asked if there was a way for us to have some sort of place where we could all put our answers to the challenge so we could look at how each other had solved the problems.
And Bart and I forget who else was helping us look for it. Well, me and Helma were definitely in the mix.
Yeah, but there was somebody else in Slack. And I'm sorry I'm so bad at names, but we are looking at...
Maybe that's where Ed comes into it, because there was definitely an Ed.
One of the Eds was definitely involved in something I was reading this week.
That narrows it down, doesn't it?
Well, Ed Tobias and Ed Ross are both crazy in this category, so it could have been either one or both at this point.
I think it was the Irish Ed. But if we have figured anything out, it'll be in Slack.
And the tricky bit, having these discussions in Slack is fun for people who are listening right now, but if you're listening to this two years from now, you're in 2025, this is not going to do you any good to try to find the thread that's no longer there back in a Slack that who knows we've moved on to another tool.
But we're going to try to see if we can figure out a way to do this in a more evergreen method.
Excellent.
[43:25] So yeah, a more time teleport-y sort of way. Wibbly wobbly, timey wimey.
No promises. Bart and I are both seeking a solution that requires us to do virtually no work to maintain.
Well, I mean, I think I was pretty blunt. I ain't got the spare time.
So it can't involve me doing time because I don't got it. Bank empty.
Right, right, right. And I'll put a little time, but I won't put a lot of time.
So we'll see. We'll see if we can figure it out.
[43:54] Yes. Okay, next on our agenda is to do our terminal plumbing. So basically, next time I would like to make our scripts behave like other terminal commands. Because that way we can do everything with our scripts that we can do with a terminal command. We can pipe things into them and out of them and all of that goodness. And therefore all of the skills, particularly the skills you've been practicing where you're using terminal commands as part of your bigger automations, all of that suddenly becomes open to you having that with your shell shell script. So instead of you having to inside some sort of automated action or inside a or what's that other tool you adore?
Thank you. Inside keyboard maestro having to type in like, you know, 400 lines of code, you can just have a shell script where the code is and have keyboard maestro or whatever reference the shell script and just use it in your commands like you would use any other terminal command, you know, echo or cat or whatever.
[44:47] And that's powerful. Like that is proper powerful. It also means if you're more in the server space, cron jobs and all these kind of things, your custom stuff becomes a first-class citizen and can just do everything else.
That makes me very happy with my work hat on.
I really like when I can do that. Yes.
You know what else I like about what you've done here with this series and very much of the Git series is someone can jump into programming by stealth and just learn the shell scripting piece. That you don't have to listen to all of JavaScript and all of HTML and all of CSS to get up to this point. You know, you don't have to listen to the first 140 episodes to learn shell scripting. So I like these little series within a series. This is very fun.
[45:36] Yeah, it was a complete accident, but I don't think we've ever had as much traction as we've had from the Git stuff.
Yeah. Oh, people love the Git stuff.
And there seems to be a lot of people on planet Earth who are confused by Git and who seem to enjoy our discussions and seem to find you asking me questions the really good way to get the answer.
Which is great. Yeah, one of the things I was telling Bart in the back channel is working on these shell scripts that I'm sitting there in the terminal working on a shell script and it is perfectly natural to then do Git from the command line.
[46:09] Where everything else I've ever done, I've always done it using a GUI.
And so I'm getting much more comfortable. I did run into a problem of I didn't remember if I forgot how to or I forgot to put in a git commit message.
And I was like, no idea how to amend a commit message. Let me open up GitKraken and fix it.
But I'm sure you taught us in there somewhere. Oh, yeah. But, you know, at least I'm doing the rudimentary stuff.
I'm getting very, very comfortable doing it from the command line.
And I feel somehow more powerful that I'm typing everything out by hand than opening a GUI. It just feels good. I like it.
I think the dirty secret of every sysadmin is that they use both GUIs and terminals.
It's not either or, it's yes and.
Right, what is the least effort to get me from where I am now to where I wish I were five minutes ago?
If it's a GUI, I'll use a GUI. If it's a command line, I'll use a command line. I'll probably use both within the next five minutes.
That is the life of a sysadmin. That is fine, that is normal, that is good.
Don't feel bad about using GUIs, don't feel bad about using the CLI.
They're all great. They're all helpful. So since this was a short show, remind me, have I talked about Keyboard Maestro and Get Together on Programming by Stealth?
Have I mentioned that?
I don't believe you have. Well, no, because you only published it in last weekend's regular show.
So you definitely should plug that installment because it's very relevant and very good.
[47:37] Yeah, so what I was looking at, Keyboard Maestro is a really powerful automation tool for the Mac, and I really like it, it's great and everything, but it's got one missing piece.
You basically can't go backwards if you mess up a script.
If you do a bunch of changes and you can't remember what you did, you just can't do anything about it.
If you, like, let's say you delete a step accidentally, you can hit Command-Z and go backwards one, that's fine.
But you can't really, there's no version control, there's no dial me back.
But a guy named Dan Thomas wrote a Keyboard Maestro macro, two of them actually, one that takes the entire database of Keyboard Maestro. That's one problem, is the whole thing is one giant database. It's not separate files for every macro you've written. So this tool separates them out into individual files. That means you can then track them in Git. So once you've got it, You authorize a repo.
You make a repo. I can't remember. Initialize.
Initialize. You initialize a repo out of this database that gets spit out.
And then as you make changes, you could do, you know, get status.
You see which one you changed, you do a commit message, and you can either just keep it locally or you can push it up to GitHub, whatever you want to do.
But then it also, he's got another macro that allows you to go back and get one that you've messed up and go back to a previous commit.
[48:55] I haven't had the nerve to test the second part, but I'm feeling pretty good that I now have a history of the ones that I—when I'm changing them. It does require a lot of discipline to run his macro, go out and do Git.
Run his macro, go out and do Git. You got to be rigorous at it.
But I've had so many macros that were working and then I screwed them up and I don't remember how to get back to where I was. And I'm really glad that this exists. But it shows that.
Git is part of my brain now, that I expect Git to be there to save me.
And that kind of made me happy. And I would expect that would make Bart happy as well.
[49:29] It absolutely does. Because if we teleport ourselves back to episode one of PBS, which is, I dread to think how long ago episode one was. It's many, many moons ago. But I think I would have very strongly made the point that the whole reason to become a coder is to make the computer work for you. Don't be a slave to the computer. Make this computer a slave to you. And these, kind of automation tools, Git, this is the power you get from being able to make the computer do your bidding. And it's very satisfying when you can make the computer do what you, want.
Yeah, yeah, that's it. It's a cross between that power and solving a puzzle.
[50:14] Right, which is very dopamine rewarding. Yeah, yeah, yeah.
Yes. And that is why I'm going to get very I'm going to jump on the soapbox.
But if you're thinking of a career, the reason you want to be a sysadmin is because you are paid day in, day out to solve puzzles.
That's your job. You go in every day and you solve puzzles. And when you get it right, people think you're Superman.
We get it wrong, not so much. You feel terrible.
[50:43] Right. Well, next time, as I say, we are going to do our terminal plumbing to make our scripts behave like normal terminal commands, and then we have a few more little odds and ends to wrap up.
And what we're going to finish with is a sort of a crib sheet for Bash that is going to be like a one-page summary of all the weird syntaxes.
Why double square brackets? What does that mean? Single round bracket, single curly bracket.
What? Right? quick crib sheet so you can jump back to. And we're going to do that as an episode of Taming the Terminal. So basically we can link to Taming the Terminal and say, here's the summary and here's the detail. And then the Taming the Terminal feed will have a single entry that encapsulates Bash with jumpouts.
That points back to all of the... That points back to all of the detail we've just done here.
That's going to be good. I like that. So then I'm going to have to print that out with my new laser printer and stick it inside my paper-bound book.
[51:45] Well, to be honest, some of these things are actually worth printing out.
I do I do have a whiteboard in work where I do have printouts of some very useful Vim commands, although actually I've picked up a great new tip.
I found a sticky notes app that stays on my desktop and I have a link to 100 cool Vim commands and every month I put a different command in the sticky and then, I make it my business that month to use that command.
So this month I learned G and capital G to go to the bottom of a file and GG to go the top of a file. They were my learnings for this month. Oh, interesting. Interesting.
[52:20] So next month I'm going to pick something else, and then at the end of every month, I know a little bit more of him. I like it. I like it. That's a way to learn.
So, until next time, lots and lots of happy computing.
If you learn as much from Bart each week as I do, I'd like you to go over to let's-talk.ie and press one of the buttons over there to help support him.
He does 98% of the work here. I'm just the stooge that listens to him and asks the dumb questions.
If you go over to lets-talk.ie, you can support him on Patreon, you can donate via PayPal, or you can use one of his referral links.
I really hope you'll go over and help him out. In the meantime, you can contact me at Podfeet, or check out all of the shows we do over there over at podfeet.com.
Thanks for listening and see you next time.
[53:08] Music.