 Well, it's that time of the week again. It's time for Chitchat Across the Palm. This is episode number 767 for April 29th, 2023. And I'm your host, Allison Sheridan. This week, our guest is Bart Bouchard's back with another installment of Programming by Stealth. It's episode number 150 of Programming by Stealth, Bart. Yay! Yeah, I feel like we should do something to celebrate, but it's just another episode of us doing some phone with Bash. So there you go. But I want to tell you a joke I told on Mastodon because I think it was just fabulous. I made myself giggle quite a bit. I was going back through the recording because the show notes explain how shift works, but I stopped you and made you go over it in a lot more detail. And you said something at one point. You said, minus a, what was it? Minus a stinky is two. And it made perfect sense in context as you're talking about different ways to shift things and everything. So I tuned it out that if you listen to Programming by Stealth and you hear Bart say minus a stinky is two, it will make perfect sense to you. And that's why you should listen to this show. Definitely. Even I'm having trouble imagining what I was talking about, but if you say I made sense, I'm sure I did. It did, it helped me understand. It was perfect. That'll do. Anyway, we should probably march forward. I do want to say that I did my homework. I started my homework earlier, but I finished it two days ago, which I think was really good because it's all super fresh, what you taught us last time. And a lot of times I've worked on it two weeks ago and I'm like, I don't know, what did you talk about, Bart? I don't have any memory. Okay, well, I'm gonna catch us all up anyway because we're trying to get to the stage where our scripts behave like, I call it in the show, it's first class terminal citizens, our scripts basically behave like grep and cat and all of those tools we're used to using. We should be able to replicate their functionality with our scripts and make them behave just like everything else. And we're getting darn close because last time we learned about how to do the minus X, minus all those optiony type things so we can have options with arguments or just flags. So minus L a limit or minus X, whatever. And what's left now is we need our scripts to play nice within what's officially called stream redirection, but what I forever will call terminal plumbing because the main symbol is the pipe. So what else would it be? Oh, okay. And it's about taking flows and connecting them in different ways. Like they are actually called streams. So although I guess you could argue you're bad at plumbing if there's a stream in the house. But anyway. What's long as it's inside the pipe? If it's inside the pipe, you're doing it right. Yes. I think we call it a flow rather than a stream. I think it's a stream when it's flowing out your kitchen down to the street. Anyway, so really that's what we're gonna focus on today is just understanding in a little bit more detail what really is going on when we do that piping. And then when we understand what it's really doing we can then tweak our scripts to make them interact with those pipes better to basically make them be more intelligent and to do smarter things with that extra knowledge we'll have picked up. So I did set you a challenge which was kind of, I mean it was sort of a, okay, all I need to make you practice something. So it was basically to take the existing menu system we have where it asks you to pick some items for breakfast and to change, we had said last time to have an optional argument that would be a limit for the amount of items you could order. And I said to change that into a terminal style option. So minus L and a number. And then because I wanted some way of having you also have to do a flag which is an option with no value. I also said a minus S to request some snark with your order. I sort of figured you'd have fun with the minus S. Perhaps more time on that than on the actual bash. Oh, crafting the exact message was like 30% of it. I was unfortunately a bit rushed. So I ended up not being nearly as intense. My snark is boring. Your snark will be infinitely better than mine. But I guess the point is my code still shows how even if it shows I'm not very imaginative. So you will find my sample solution in the zip file as pbs149-challenge-solution.sh, again, super imaginative. And almost all of the code really is the same as last time. But the difference is at the top of the file, I use get ops as we learned last time to process my arguments. And so it's extremely by the book. So I guess the things to point out is that get ops needs two arguments. The first one is this cryptic string that tells get ops what your options are. And then you have to pick a name for the variable that's going to hold the current option being processed. And I said, look, to save your sanity, just always call it opt. I'm always gonna call it opt in the show notes and I recommend you do the same. So while get ops and then I have the string colon L colon S and then opt. So the colon L colon S is where it's revisiting to help cement it in because that's the cryptic weird bit. Can I say what it means? Please. So the first colon means I wanna suppress the error messages that are gonna come out of bash and I'm gonna write my own. And the second colon, which is after the L tells you that L is an option, not a flag. Correct. And the S is a flag. Because it has no colon. Now I thought it was interesting that you put it in that order because I think of it the other way. I think of it is that I would have had a colon S L colon. I like the alphabet. Just as I think of the, what's that? I like the alphabet. Oh, you did it, alphabetic. Oh, wow. No, I did it in that I'm used to seeing flags first and option second. Like I said, it's irrelevant here. I do it in that order. As long as I need them. But it was interesting to me that we did it different ways but I still understood what you did. Perfect, perfect. And for the rest, my code is basically the same as it was last time except everywhere I have an echo statement. I have an if statement saying if there is snark say this, else say the normal thing. But other than that, reading my code is exactly the same as last time. So the key point was the get-offs which is what I was hoping people would practice. So another big piece of that was the shift. And that was the part that I didn't, I understood logically what you were trying to tell us in the show notes but that's why I actually went back and listened again because I stopped you and I said, well, I don't, what do you mean? What do you mean? Let me get specific. So I actually wrote up a little thing for myself where it just says, okay, if they passed a name, then it would be dollar, that would be in dollar one, or sorry, name would be in dollar one if they had passed a flag. And then I wrote out each of the different scenarios and then I could, I said, okay, if I was doing it mathematically, how would I do it? And that's why it becomes that opt in minus one but I understood it because I walked through the steps and wrote it out. And so I made a point of using shift and it was surprising to me, I'd have to go back and think about why I used it and you didn't, but you didn't use shift in this at all. And that was, to me, that was like half of the lesson. Well, you only need to do the shifting if you have normal arguments after your options. So if you have unnamed arguments that you need to collect afterwards. So in this case, we have a minus L and a minus S but they're both options. So they're both covered by get up and there actually is nothing else. There is no- Don't say get up, don't say get up, let's get up. Yes, it's not French, I have to pronounce the S. Yes. So what do you mean by normal arguments? As in a dollar, one dollar, two dollar, three. So if you have, like think of say grep, you would say grep minus V to go into inverse mode and then you would say what you want to grep and where you want to grep. So it's grep minus V, some pattern, some file. Well, the some pattern, some file are just normal arguments, they're not named arguments. The minus V is a flag. I don't know what that has anything to do with this looking at a menu and setting a limit thing. So talk in the context of this. Okay, but in the context of this, the shift is unneeded. We have no unnamed arguments. There are no arguments that don't have a name. Limit minus L has a name. The flag has a name, they're L and S. There are no arguments that aren't minus something. So why did you teach it to us last week with literally those same kind of flags and options? Because sometimes you want both. You need to have flags and options and normal arguments. So in the example in the show notes, there were normal arguments too. I don't remember what the example was off the top of my head, but the example we did in the show had normal arguments too. So why did we learn it last time? The point of learning it was so that we could write scripts not so that we could do the example. But the example that you gave in the show notes last time did require it. And what we did was very, very similar in the homework, was very, very similar to what you did in the example in the show notes last week. In the example, we needed normal arguments as well. In the example, there were arguments that didn't have a minus in front of them. There were regular traditional command line arguments. And the whole big reason, the whole reason we did the shift was because otherwise the position of those normal arguments would be bouncing all over the place, depending on which optional arguments we'll put in first. So I have no idea what you're talking about still. I don't know what you mean by normal arguments versus anything else. The example we had was base name zero minus s minus a adjective and then name. Which one of those are you calling a normal argument? Name. Name is not a minus anything, name is just name. Okay, what was name? Name is the first argument. Okay, before we learned about options, like forget about options, forget about the minuses. We just had name or script, space argument one, space argument two. Okay, I forget what name was now. So now I don't know how it was different than the other arguments. I clearly thought I understood this completely and I clearly don't understand it. Okay, go back in time and just think of the most ordinary script possible. It just takes an argument that is your name and says hello your name. We would simply say name of script, space, the argument, which we would call name. And then we would say echo, hello dollar one. Okay. Right, so that's a normal argument. It's just- Right, but if it got shifted over because of the dash s and the dash a in its adjective, we had to use- Bingo. We had to shift in order to know what name was. Correct, exactly. But in our menu, there are no name-like normal arguments. There's only options. So there's nothing after the options that we have to worry about where it is. So I guess what I did wasn't wrong, but it wasn't necessary. Yes, what you did- I found it by its dollar one after I did the shift, but I didn't need to do that, you're saying. Well, what did you find by the dollar one? Well, my code is all different so my names wouldn't make any sense. Right, but neither the limit nor the snark. Maybe I just shifted it and I never used it. That seems- That's all together possible. I think so, Alison, because there was nothing else to fetch. No place to go get it, yeah. I think that might be what I did. Okay, well good. Well, I'm glad I asked. So what you actually did then was you would have shifted away all the arguments and- I cleaned them all up. You cleaned them all up. They would have all been, yeah, exactly. They would have all been gone, which is fine. I have to think about that. Okay, I'm now beyond what's helpful to the audience, so let's move on, but I'm glad I asked because I didn't actually get that subtlety till now. The chances are that if you're asking the questions, at least A listener is. At least A. If not now, sometime in the future. So- Right, okay. Now today we are moving on to look at stream redirection with our scripts. And we've done this before intending the terminal, installments 15 and 16 to be precise, but we did it from the point of view of a terminal user. Well, now we have a different hat on. We're now a script author, we're now writing scripts. So yes, a lot of the same knowledge applies, but it is a different point of view. It's like writing program versus using a program. They're not quite the same thing. So there are a reminder to the audience that Bart and I did a podcast called Taming the Terminal that's got something more than 42 episodes. We keep forgetting to go back and look out how many there are, but it's another podcast you can listen to that is strictly all about the terminal. And I'm actually really glad to see you're going over this because that material is pretty rusty for me. It's been a while. I should have checked the data actually when I was going back finding the episode numbers, but there are low episode numbers because that's 15 and 16 and we know we did at least 43 of the other show. So that means we're going back a long way here. So if you want to remind yourself of what it was like from the point of view of a user, those two installments will go over in great detail. You can hear me now and talk about it. I'm sure we had great fun, but today we're looking at the same facts from the point of view of a script author. But we've got to do a quick little recap because we do something to know with the same, but there is a shared reality here. So in Unixi world, which includes our Macs and anything that does POSIX, there are three standard IO streams, IO being input output. There is a standard input, which has the numeric ID zero and is known as STD in, in all caps. There is the standard, now by default, and then notice the word is by default, STD in is hooked up to your keyboard. So if something reads from STD in, it's reading from your keyboard by default before we start plumbing, right? Before we do any plumbing. So STD in again is standard in and has the value of zero, numeric ID zero. Yeah. The second ID, which is one because Hay Linux counts from zero as those Unix. The second one is called standard out or STD out and that is by default connected to the terminal. So anything that goes to standard out just appears on your terminal window as a line of text or whatever. So if you say echo whatever and it shows up on the line below that's standard out. Or that is, yes, that is standard out. Standard error has the ID of one and is STD error. It is also by default plumbed to the terminal. So when you... Wait, they have the same numeric ID. Standard out and standard error both one? No, that's a typo. It's zero, one, two. Thank you. Zero, one, two. Fixed. Yeah, thank you. They do count. And you said one again, so repeated. So standard in is zero, standard out is one and standard error is two. And by default out and error go to the same place which is your terminal. But when we start doing the plumbing we can bifurcate those two. Which is, well, we'll see why this is useful. We'll actually, I will prove to you that this is useful to do. I won't just say it, I'll show it. So that's by default, keyboard to terminal. Well, there are three groups of operators. There are the ones for redirecting output to a file or something else which all start with a greater than sign. So I'm sort of grouping them together as like greater than and friends. Greater than is the most important one. There is the inverse which is for pulling input from a file or something similar and they are the less than and friends. And then there is one which sort of does a jiggery pokery. It takes the output from one command and makes it become the input of another command. So we're effectively wiring the output from one to the input of the other and that is the pipe symbol. And that is my favorite. Probably the most used of the three is the pipe. So as the output of one gets connected to the input of the other. So when we're using a redirection operator the input stream isn't the keyboard anymore necessarily. If I connect the output of say a grep command to be the input of a word count command then as far as a word count is command standard in is not your keyboard. Standard in is whatever grep sent us. Let me pause you for just a second. I understood what you just said but I'm gonna be stuck a little bit on the difference between redirecting and with the greater than symbol and sending the output of one command to another with pipe. Those both sound like redirecting. Well, they are redirecting but one of them is redirecting to storage and one of them is redirecting process to process. Oh, okay. So redirecting to storage is the greater than symbol. Like if we were to push it to a file for example. Yes. But pipe is saying I'm gonna take this command and I'm gonna run it into this command. Yes. So if it's two or from a file it's your less than or greater thans and if it's between commands it's your pipe. Okay. We've talked a lot about when you're searching a file saying cat the name of the file and then we pipe it to the grep command where we're gonna search for something. Yes. So we still haven't touched it. We haven't saved it anywhere. In fact, it's gonna go to the probably just to the terminal in that case but that'd be an example of sending a process to process. Yes. Which is by far the most common, right? Okay. You tend to chain process to process to process and maybe at the start or the end you come from a file or go to a file. But most of the time you're going process to process process. And good. I always think of our dear departed friend Tim Verporten who used to talk about Mac apps that did one thing and did them well. That is the philosophy of Linux and Unix. You have terminal commands that do one thing and you as the terminal user string them together to achieve your bigger goal which could be completely different to someone else's bigger goal. So the idea is we give you the Lego pieces and you stick them together and the way you stick them together is with the pipe. So you say, well, actually I want to count all of the lines that match whatever. You take a grep and you pipe it to a WC-L, right? That is how you're supposed to think in terminal ways, yeah. Right. And that's all connecting these, changing the meaning of standard in and standard out for everything. So let's see it in action. So we have our installment zip and it contains my sample solution. So let's use that to play with this redirection stuff. So the first operator I want to play with is the less than sign for changing, basically sending a file to become standard in. So that you will find in the zip file, there's also a new file called favoriteorder.txt which contains two, three, one on separate lines. I'm going to do the worst thing for the audience. I'm going to put you again. I just looked in installment resources for me. The zip file's not there, which is often the case, but installment resources, the folder's not there either. So I can't follow along without pbs-150. I pushed both of those. I see pbs-149 in installment resources and I see docs. Sorry audience, this has no value for you whatsoever. But if I can't follow along, I'll just be so confused. Can you be sure you pulled the latest pbs-150 whip? I can, I will double check that. So I am opening for the audience's excitement. I'm reopening Git Kraken and I'm pulling again. It says pulled successfully, already up to date. So feet are fine pbs-150. And you are definitely on? Nope, no, I'm trying. Okay, but I can't check out pbs-150. I'm on master. Double click on 150 and I'll jump into it. I am and it says check out failed. One conflict prevents check out, commits stash or discard your changes. So that I'm gonna have to discard my changes, but how do I do that? Cause I changed this file. So that's why it's not letting me do that. Oh, poop. So I don't know how to discard my changes. Okay, so in output at the top, you have your currently, your circle, which is all dotty cause it's not finished yet. That is your current changes. So if you click on that and you're at a very, very, very top of your timeline. Very top, yes. So if you click on that, and then in the sidebar it'll show you the changes and just go discard. Just to right click the little trash can. There we go, reset all. That's easy to do. Okay, now I'm gonna pull this. Oh, there we go. Some stuff jumped around. The thing I changed is not a big deal. I can fix it. Sorry audience, here we go. I've got pbs-150. Thank you very much. Sorry for that. I think it's great for people to hear our git faffing about because we all do this. I have often found myself going, but the universe makes no sense. What is the universe? Oh, I'm on the wrong branch. I'm three weeks ago. Yeah, three weeks ago. This is how things were. Okay, so I am opening a terminal in the folder that was created by the zip. Is that where I wanna be probably? Yes, please. Okay. And so in there is now a file called favoriteorder.txt. Got it. Which contains each in its own line, two, three, one. Now, if you run my sample solution normally, you will notice that option two in the menu is pancakes, option three in the menu is waffles, and option one in the menu is done. So now, rather than just running my sample solution as it is, we're going to say dot slash pbs149challengesolution.sh less than sign favoriteorder.txt. You're gonna tell us why we're writing this? So we are taking the content of favoriteorder.txt and using it as standard in, right? The less than sign is redirecting that file to become standard in. So instead of reading from the keyboard, our script will now read from that file. I don't understand what we're doing. I don't know where we're going or why. Dot slash pbs149challengesolution.sh runs our challenge solution minus sign favoriteorder.txt redirects that file to become standard input. Does favoriteorder.txt have nothing to do with when pbs149 runs normally? Does it normally run using a different file? Okay, didn't know that. Don't know every, don't have yours code memorized. So you were normally pulling from, it must be menu.txt. Oh, this script is still running pulling from menu.txt. Favorite order is completely new. Favorite order, I have made it up this second in time. It has nothing to do with the homework. I have literally just created it right now. Brand new. Okay, and it's got three numbers in it. Two, three, one. What is it doing? I did, what is it doing to say, here's your script less than favoriteorder.txt. What is that doing? That is redirecting the content of that file to become standard input. So instead of us answering the question, what do we want in typing two, three, one, favoriteorder is typing two, three, one. Yes, favoriteorder has taken the job of the keyboard. We have redirected that file to become standard input. So if you hit enter on that, it will immediately just jump to the process and tell you you have ordered pancakes and waffles. And then said done with the one. Oh, okay, gotcha. The important point that less than is cuddled to favoriteorder.txt. Maybe not be important. You assume that's important? Not 100% sure, actually. I wonder what to find out is to take this to... Well, I'll do it. And cuddle it and see what happens. Nope, it worked just fine. Yeah, I was 80% sure it would, but... Okay. Easier to test. So that is an example of redirecting a file to become standard input. Okay. We can do the input. I noticed standard output was normal. It just, it's still spit out, you added pancakes to your order, you added waffles to your order. Okay, you must have said done. Correct, because we haven't touched it, right? There is nothing in that there that jiggers about with that until right now. Now we're gonna jigger about with the input. Why did I have a feeling you were gonna mess with the other side? Okay, so we can run the same thing again, but this time we're gonna have another space and then a greater than sign to transcript.txt. So now we are redirecting standard out to a different text file, transcript.txt. Oh, look at that. I see a file there. So... Yeah. If you look at the content of that file with cat transcript.txt, you will see it says choose your breakfast, added pancakes, added waffles. You order the following two items, pancakes, waffles. Do you notice something else? It's got some empty line feeds in it. What's not in the file? It's not in the file is the list of options. They were still in your terminal. Right. So your terminal wasn't blank. Your terminal showed you the menu. It didn't show you making your choices. It didn't show you your final order. So the stuff that was shown in the terminal is not in the text file. And the stuff that is in the text file was never shown on the terminal. Okay. So do you remember I said that? I said I watched it more carefully. I'm looking back at it right now. Well, let's move it again. So I still see on screen done pancakes, waffles, all the different choices. And then I see a question mark, question mark, question mark, and then a percent sign. So that's the stuff in the middle where it was talking to the script. The script was talking to, sorry, the script was talking to favorite order and to transcript in between there. But so it was spitting some of it out. Yeah. What does that mean? Right. So why do they get bifurcated? Which remember I said that by default, standard error and standard out go to the same place. So you never noticed that the way the select loop works is it does its communication about what it's asking you to standard error, not to standard out. And the reason for that is that you can pipe the results of your script without all of this questioning gobbledygook being in the output. So you could grep your order for pancakes or whatever. And all of the menu and stuff would not be in there because it was written to standard error, which is still going to the terminal. We didn't know until just now that that was being written to standard error, correct? Correct because that's the revelation. Okay. There we go, right? Because it didn't make any difference when we were in default world because in default world, standard error and standard out are both the terminal. So you don't notice which is which. Until you redirect one, and when you redirect just the one, then all of a sudden you realize that, oh, there were actually two people talking to me here. They just appeared in the same place. It's like having a telegram group chat and not realizing it's a group chat. You just thought all the things were coming from the one person, but actually they were coming from two people. They were coming from standard error and standard out. So it's actually very powerful to make intelligent use of error and out because if you have output that makes sense to go into the pipe to the next command, that should go to standard out. But if you need to communicate to the user on like a side channel that isn't really part of your script's output, you can use standard error as your side channel. So we actually need to clean house a little bit here because we're telling people, choose your breakfast as many items as you like to standard out. But actually that's kind of a part of the options we're offering people, right? So that actually should have gone to standard error. And the only thing that should have gone to standard out is what the script actually did, added pancakes, added waffles, and then your order is to be consistent. Let me see if I can say that back again. So in our transcript.txt file, it says choose your breakfast as many as you like, added pancakes, added waffles. And then it says you order the following two items, pancakes, and waffles. You're saying that final part makes sense to be in the transcript, what got ordered. But the choices and that should have stayed over in standard error or been over in standard error. In my opinion, the added pancakes is fine as well as output, as is the added waffles. You could have an argument, you're right, they could be in either place. But to choose your breakfast is a heading for that menu, which is not in standard out. That heading really should be with the rest of the menu. Okay, so how do we know how to make things go to standard error versus standard out? Bing, bing, there we go. Thank you, Alison. You have set me up perfectly. I'm feeling that was a softball caring tip up for you. Up until now I have been describing what is happening. Well, now let's switch gears. Remember I said we're looking at this from the programmers hat? Okay, well let's put the programmers hat firmly in our head. How do we tweak our script so that we have better interaction with these two streams? We can use stream redirection to tell just the echo commands that we want to redirect to go to the other stream. Now the syntax for this is one greater than ampersand two. So one is the ID for standard out. Ampersand two represents standard error. So we are saying take one and shove it to two. In other words, take standard out and make it be standard error. And we stick that on the end of just the echo commands that we want to go to the other place. How interesting. I like it's very clean looking and it seems to make sense. It's a little odd that it's got an ampersand but I'm beyond questioning where these weird symbols just fly in for no reason but yeah. So basically we say echo choose your breakfast as many as you like, one arrow ampersand two. Then when we rerun, so I changed my script that all I changed was that ampersand one and stuff. So PBS 150-menu.sh is our tweaked version. And so if you run it with less than favorites greater than transcript, now we get a much more sane situation where the heading for the menu and the menu go to standard error. So they're still in your terminal but what actually happened has gone to standard out. So it's in your transcript. So your transcript says added pancakes, added waffles. You ordered the following two items, pancakes, waffles. So I want to go back and look at your script because I don't know where you put these one ampersand, one greater than ampersand two. Okay, so you're talking about what are you asking for? Basically all of the echoes? Any of the echoes are the one that says your order at the end. And the one that says added pancakes to your order. You could make an argument that that should have gone to standard error too. That's a philosophical argument. I won't say you're wrong or that I'm right. I just chose. It's very odd about this output that is disconcerting is I'm seeing a percent sign at the end of things. I wonder if that's something goofy I've done. Like when I run that pbs158 menu.sh. I think it's a side effect of- Input is favorite order. I think that's just a side effect of us doing the piping in because if you don't do the pipe in you sort of get the normal interaction because that's the next thing you can do, right? There's no pipe in this. We didn't do any piping. Sorry, not piping, my apologies. We're doing the redirection of the input and the output. So it's automating the answering of the question. So I think that's where some of this stuff is happening from. Okay, okay, that's just not our- The next thing we have in the show notes is to take out the less than favorite order and let us actually interact with the script. So even though our output is going to the transcript file because the questioning is happening to standard error we can still interact with the script without messing up the transcript. So we can actually run this interactively and just pipe the output to the transcript. So we can still actually answer our questions in real time. I'd like a pancake, I'd like some muesli and then when we're finished, when we hit done then we get our transcript. So question, we have written to that transcript once already. If we run this command again without the input of favorite order it's going to write over transcript.txt. It is going to write over it because as you had learned in TTT 15, I think, or 16, doesn't matter. One arrow means replace, two arrows means append. So if you did arrow, arrow transcript, so remember I said it was greater than and friends. Well, the two greater thans is one of the friends and the two greater than means append. Okay, so if you're writing a log file, you very often use arrow, arrow because then you're sort of building up your log file. Yeah, yeah. The key point I'm making to you is that it's important when you're writing your scripts and they're going to play nice with stream redirection that you think carefully out or error. Where do I want this piece of information to go? SCD out or SCD error? And basically make a conscious decision and what you decide is up to you as a programmer, right? But think about it. So the takeaway here is think about it. It's like you can use a scalpel on this. You're not just blood forcing your way through it. You're saying, okay, I'm going to take this piece, put it there, this piece, put it there. So if you don't put it, if you don't, the echo statements for example, if you don't put this one greater than ampersand two at the end of the line, you're actively saying it's going to go to standard out. So standard out is default. It's going to go to standard out. Okay. Yeah. Yeah, so you proactively choose to go to standard error. So standard out is what happens if you don't think about it. So if you echo or whatever, it's all going to standard out. In fact, the only command you've learned about for going to standard error is echo, but we will soon learn about a much more powerful one called printf, but for now it's just echo, you know. Oh, I've actually used printf in one of my answers already. You're ahead of the class. For a very specific reason. Well, printf is very powerful. Okay. So we've now looked at two and from files. So obviously the last remaining piece here is the pipe, which is you're taking the output of one command and making it be the input of the other. So we can already see this in action by, if we take the echo command, we can say echo minus e for don't get rid of the new line characters, please. And then the string two slash n three slash n one. In other words, we're going to echo out two, three, one. We're going to then pipe that to pbs158-menu.sh. And what you will see is that that is entirely equivalent to taking the file with the same content with the less than sign, right? We're just saying print this and shove it here. Okay. So the pipe has said the output of the echo becomes the input to the script. Okay. And so you could only do it with the less than symbol if you had a file to shove in. Yes. Something from storage, as you said. Something from storage. So that is the difference, right? The pipe is a live thing to another live thing, whereas the arrows are a static thing to another, you know, to or from. Yeah. Yeah. Okay. And of course, we can keep piping stuff, right? So anything you echo is going to standard out, but that standard out can become standard into another command. So how many lines are there in the result of running our script? Well, I can pipe that to wc-l for word count minus line. So echo minus e2-n3-n1 pipe, our script pipe wc-l will tell us that there are six lines in that output. Right, right. Not very exciting, but yeah. Now, we can still see what's going on on standard error, which is fine on the terminal, but what if we're running this through a cron job or something and every time the cron job gets output, it sends you an email saying, oh, this cron job did something. It said something, I can't tell you anything, so I'm going to send you an email to tell you cron job did something. What if you want a silence? Remind people what a cron job is before you go too far. So a cron job is an auto, it's cron for cronog- Chronological? Chronological chronometer time. Chronology, that's the root word, chronology is where I'm trying to get to. So it's a thing that's happened at a time. So it's a command that runs certain things at specific times. Yes, and the detail is complicated. Yes, but it's a way of saying at five o'clock every day, do this, and you don't really want to send you an email at five o'clock every day going, hey, this script I ran for you, it made some output. Hey, this script I ran for you, it made some output. Hey, this script, all right. So you may want to silence stdr. You can do that. There is a special black hole built into every Linux computer and every Mac, it is dev null. So slash dev is where your devices go when the null device is just an absorber of data that makes it go away. Plus devs.null is a black hole for data. So we can say two, which is the magic number for standard error, greater than sign dev null. So what that will do is it will take just standard error and shove it to dev null. Now, this all fits in our bigger pipeline, so let's zoom back out a bit here. So we have echo space minus e two slash n three slash n one. So that's going to print to standard out two, three, one on separate lines. That's going into a pipe. So that is standard out is two, three, one, that is now becoming standard in to our script dot slash pbs 150a menu that SH. Then we have a two greater than dev null. So that's saying standard error for our script is going to the black hole. Then we have another pipe. So standard out from our script is becoming standard in to something else, which is WC minus L. Right. And now instead of our six being mixed up with all of the standard error output, the six from the word count is all by itself because standard error has vanished into a black hole. Okay. That's interesting. That's quite a complicated looking command, but the way you broke it down is really clear. I'm not sure tomorrow I could reproduce what you just said, but I think I follow it. Yeah. And it does take a bit of time to break them down, right? So it's every time you see a pipe, its input becomes output. And every time you see an arrow, it's like, I'm going somewhere. What am I, what's going where? If there's no number, it's this standard out. And if there is a number, it's whatever the number is. So to arrow means standard error goes to and dev null could be like error transcript or error dot log. So if you only want your errors, right, sometimes actually you don't care about standard out. So you could actually say just arrow. So instead of saying to arrow, just say arrow dev null would take the standard output to the black hole. So if you have a script that you run with cron and as long as there's no errors, it's like, well, I have a two outputs. So if I run it interactively, I can see what it's doing. But if I'm running it automatically, I don't care about okay. I don't care about it saying, I'm happy. I'm shiny. The only thing I care about is, oh, I'm cranky. Then I take standard error. Okay, so standard error would still be output if you just went directly to dev null with the output of your script. Yeah, exactly. So if I take the output of the script to dev null, I can then say that. So I can say to arrow error dot log, arrow dev null, which means they're separate. I can control them separately, very powerful. By the way, every time I hear the phrase dev null, I remember that in 1996 Leo Laporte was on an MSNBC computer and technology series called The Sight and he had a virtual reality character and its name was dev null. While I was a grad student, I had the pleasure of spending three weeks working in the Goddard Space Flight Center as a student, as a visiting student. And the sysadmin was a really cool guy and he worked for NASA and he was a sysadmin. I was going to like him. And we went out to the parking lot, he gave me a lift home and his number plate was dev null. Yeah, nice. Oh, wow. So he's the license plate for you Americans. Yeah, I was so impressed. Like I was already impressed sysadmin and NASA. Sysadmin and NASA who drives the car with the license plate dev null sold. Man's a legend. Anyway, okay, so far so good. Now, I need to tell you something else that we have been implicitly using, but I haven't said it explicitly, so now I'm going to say it. Every command in your script inherits your script standard in, standard out, and standard error. So when you redirected something to the script, every command run by the script made use of that. And we saw this because the read command, sorry, the select in command was able to read from the file. It inherited that fact from the script. What do you mean by inherited? So the read command, if you just type the read command into the terminal, it will read from standard in, which is the keyboard by default. So how would a read command inside our script know that it shouldn't be reading from the keyboard if we use a narrow symbol? Well, the answer is because everything inside your script inherits the script's standard in. So the whole script gets a new standard in, including every command in the script. Okay, I feel like this is a profound statement and it sounds sort of obvious. It's the opposite. I'm explicitly saying the obvious. It's not magic. It's that the script, everything inside the script inherits the whole script standard in. It's not, it's important, but it's not complicated. Just subtle. Okay. Yeah, it's just, it's important to say explicitly. This is why the read and the select statements work because they're just inherited from the script that they're within. The other thing to say is that these streams are like streams. If you take a cup of water out of a stream, it is gone from the stream. If you read a character from standard in, it is gone from standard in. So you can't use standard in like you use an argument. You can read $1 50 times. If you read from the stream, it's gone from the stream and you now have it. So you better do something with it. If you read from the stream, you either have to, if you're using it as the input to a select statement, well, then it's done its work, right? You've chosen pancakes or waffles. But if you're reading from the screen, if you're grep and you're reading from the stream to search, well, you'd better save that to a variable somewhere. So you can actually work on it, right? And then return the appropriate output. So we need to actually capture standard in. And so we already know how to read a file. We already know how to read from the keyboard. So that's actually reading from standard in. So the read command reads one line from standard in by default. What it really does is it keeps reading one character at a time from standard in until it meets the value of ifs. Dollar capital I, dollar capital F, dollar capital, sorry, dollar capital IFS is the input field separator. By default, it's the new line character. So the read command uses ifs to know when to stop. So it takes a sip from standard in. Are you the end of, are you my separator character? No, I'll take the next character. Take the next character. I'll take the next character. When I meet my separator, I stop on whatever I've gotten is in the variable you told me to put it in. So you say read name a variable. So it keeps reading until it meets its separator, which by default is new line character. So by default, read reads one line from standard in. So that may or may not be everything in standard in, but the read command will stop. So the stream is paused and there may or may not be more in the stream. But what you've read is now not in the stream anymore. Okay, right. Right, now the read command puts it in a variable, so you're okay there. And you could, if you want to read everything at once, right, you could use the read command inside a while loop and just keep reading it one line at a time, one line at a time, one line at a time. Or you could change. Better put it someplace. Yes, which means you have to have it inside a loop so you're appending it to a string or something. I mean, we could do that. Or you could change the value of ifs to be the empty string, which means there is no separator. In other words, I'm going to keep reading until the stream is a dry. Right? That works great. But the problem now is remember to put ifs back because otherwise you've made spooky added action at a distance for later down your script. And I don't trust myself not to mess that up. So I never... That's why you panicked when I did it in a script a few weeks ago. Yeah, I hate debugging people. I hate debugging other people's code that uses ifs because I'm always afraid it'll bite. Maybe the reason they're asking me for help is something to do with that ifs at the very top of the script and we're down at line 400. And that's where the problem is manifesting. I always make me worried. So what if we want to achieve the same end in a simpler way? Well, there actually is another command we've already met that will read standard in until it runs out of standard in. And that is the cat command. If you run the cat command with no arguments, what the man page says it does is it reads standard in and writes it the standard out. So if you just say cat, it just says, give me standard in and I will shove it the standard out. Just a little redirect through cat. So garbage in, garbage out. Or in this case, standard in. So we already know that we can say dollar open bracket and what that means is take the result of running this command and save it to a variable. So we can say waffles equals open dollar grep whatever close our thing. And then the result of the grep goes into the variable waffles. Well, basically that standard out becomes the value of the variable. So if we just say dollar open bracket cat close bracket, that will capture everything in standard in and shove it into the variable, everything, all in one go. And the wonderful colloquialism for that is called slurping. You slurp standard in into the variable. Just slurp the whole stream into the variable. So we have, you will find there is a file called pbs150bnaiveshouter.sh. This will make sense in a moment. So this is a very simple little script that takes at slurps using cat, all of std in, converts it to uppercase using the translate or to command, which there is a link to the relevant documentation in the show notes. And then it writes it to the standard output. So basically you give it some input, it converts it to uppercase and gives it back to you. Or it shouts at you. I mean, we said that if it's all uppercase, we call it shouting. So the script just says the input becomes equal to dollar cat. Then we say the output becomes equal to we have dollar open bracket echo the input pipe to or lower to upper. So the result of that translation will be stored in the output. And then we just echo the output. Just for clarity, since you said among the second command, the input equals dollar parentheses cat. Thank you. Not just dollar cat. That is very important. So the dollar parentheses means whatever's in here, the output of that goes into the variable. The output of that cat will be. I've forgotten that. Whatever was on std2. We're familiar, but I forgot it. Yeah. Okay. So basically that's saying take standard in and shove it into that variable. So if we now, if we now on our terminal type echo, hello world pipe dot slash PBS 150B naive shouter, we'll see hello world. Shout it back at us. It works. She sounds surprised now. Why did I call this naive shouter? Try run the script without the pipe. Just dot slash PBS 150B dash naive shouter. Now what does it do? Let me see. Copy because I'm too lazy to do it. It doesn't do it. It gets stuck. It gets stuck. Why is it stuck? Well, standard in is now the keyboard. Cat is sitting there going, I am going to read keep reading until I get to the end of the input. And the end of the input is the null character. Well, the keyboard hasn't sent anything yet. So cat is still sitting there. So you can type away. Type in something and hit enter. Yeah. Hitting enter won't end it because enter is not the null character. No. How do you send the null character from a keyboard? Control D. And now whatever you typed in will come out shouted. Sure did. Well, that's not very user friendly. Right. Oh, good. So I don't have to remember that control D. Well, you may find that useful if you accidentally get yourself caught in oh sugar, I should have poked something to this. You actually can just type away and then hit control D. But usually it's the sign that you're doing something wrong. So how do we make our script be a little bit friendlier? How do we make our script behave say like cat or grep, which can both read from standard in when they need to. And yet they can also not read from standard in when they don't need to. So let us intelligently slurp. And let's slurp with intelligence. The most obvious thing there's another great to let's slurp with intelligence. So I know you're going to ask me how can I tell whether or not there's something sitting in standard in? Because that is the first thing I thought. And I then spent two days trying to answer that question in a way that worked completely cross platform. And the answer is if you want your script to work everywhere, you can't. But the reason you can't is because you don't need to. But it took me a little while to think outside the box there. I can't find the right answer to my question. And I asked my question 50 million ways. Maybe I'm asking the wrong question. I took me two days to arrive to that realization. So then I went, why don't I read the man page for grep and cat because they behave in the normal way. So why don't I read the official description of what they do and then see how easy that is to emulate? Can you repeat the question you were asking? So I wanted to know how can I write an if statement that will return true only if there is something in standard in? In other words, can I tell if standard in is the keyboard or a pipe? Okay, because I don't know why we want to know that. Well, you saw what happens if I just pull from standard in when it's connected to the keyboard, it locks everything up. Until you rotate the type control. A way to say don't do that. A way to say don't do that, yeah. And so I thought the way to do that is to detect whether we're a pipey or whether we're a keyboardy. Right, right. Okay, I'm with you now. And it turns out that you can't do that cross platform. You can solve the problem individually in different places, but you can't solve the problem universally. If you're on a modern version of Bash, read minus T zero will give you a success response code if there is some data to read. And a failure response code if there isn't, and it will read zero pieces of data while doing so. So it's like read, it's like test reading. Read nothing, but tell me if you would have succeeded had you tried. Okay. And that works on Bash five and higher, which means it'll work on your Linux land, but the Mac is on Bash three, because Apple have a real problem with the GPL version three. So they're sticking on Bash that has GPL version two, which is Bash three. Anyway, it doesn't matter. The point is that's the wrong question. So how does cat work and how does grep work? Because if our script behaves like cat and grep, no one can tell us we're doing it wrong. They are like stalwart commands. If we emulate them, at the very least we're not being silly. So I read their man pages and it turns out their algorithm is actually really simple. Point one, if zero files are specified, read from standard in. So if you say cat on its own or grep on its own, without telling it what files to grep or cat, then it will go read standard in. If you provide it with a pipe, it will follow the pipe. If you didn't, it will wait for you to type something at the keyboard because you want me to concatenate something or to search something. I need me a something. So I'm just going to wait for standard in. Not okay. Now, what if you want to grep both standard in and some files? Or what if you want to concatenate a file with standard in? How do you tell it I want this file and standard in? The answer is they both implement the same convention. The filename minus shall be interpreted to mean read standard in. The filename minus, okay. So if you say grep some pattern minus, that means I want to find that pattern in standard in. Now grep can take 50 files. So as soon as you hit enter, it'll wait for the keyboard. Or if there was a pipe, it'll just read from the pipe. Yeah. It'll use standard in, which may or may not have been piped to, right? You just said minus was going to send it to standard in, which is the keyboard. No, no. Minus tells grep read from standard in, whatever it is. You don't know what it is, right? Grep doesn't know what it is. Remember, you can't tell whether it's the keyboard. Now all you know is it is standard in. So when you tell grep minus, you're telling grep go read standard in. It might be the keyboard. It might be a file you're piping with. It could be the output of another command. It could be coming from a file with the minus sign. It doesn't matter, right? Standard in. Okay. I'm sorry. Sleepy today or something, but I'm not following. What would it do if you didn't give it the minus, doesn't it? I thought it went to standard in. Okay. If you say grep. And now you're saying give it minus so it'll go to standard in. No. If you give it nothing at all, it says, well, I guess I'll use standard in because you haven't told me anything. If you give it one file, it will read that file. If you give it two files, it'll read two files. If you give it three files, it'll read three files. What if you want two files and standard in? What if you want to grep the output of one command? So you have some command pipe grep and you wanted to search your output and you wanted to search some other stuff. How do you have your cake in eat it? How do you both tell it use these files and use standard in? The answer is you use the file name minus to say standard in goes here. Okay. I think it's just gotten too abstract for me because I can't picture wanting to do all these things at once when I'm just trying to figure out what standard in is at this point in my brain. Okay. Try the cat command. So the cat commands job is to take multiple files and concatenate them into one output. So imagine you want to inject a header into a file. So you have your header sitting in standard in because you've pulled that header from somewhere. So the header is in standard in and the file you want to stick the header on is somewhere else. How do you tell cat take standard in, stick it on this file and then give me the result? The answer is whatever you want pipe cat minus space name a file. In other words, the minus is standard in goes here. Wow. Back to another question. I thought minus was saying, I thought minus was the name of the file. I thought that's what you said. Okay. But you just said minus space name a file. That's redundant. Right. So we're doing two, no, no. We have a file and... Okay. Let me back up. We have standard in. Okay. Imagine you have two file, right? File one and file two and you want to stick them together. You say cat, file one, file two. And cat will take file one, take file two, stick one on the other and give you the output standard out. Okay. Now, what if you want whatever is in standard in followed by a file? How do you tell cat I want standard in as my first input and I want the file as my second input? So you give it a minus to say there's a file name coming? No. You give it a minus to say show standard in here. And the file name goes wherever you want the file. Okay. I'll just read the show notes a bunch more later because I don't think I'm going to be able to get past this one. This is, it's very abstract to me. Maybe there's an example coming. There is. Well, I have no idea if the concept doesn't work. I don't know. I don't know if it's going to have... Well, look, we're going to find out. We'll have a go. So minus... Yeah. Let's just keep going. It's very important to say, it's very important to say that minus on its own will not confuse get-ups. This is good because it means we can have this convention that bash and grep and cat use will not fight with get-ups. So we could... This is good, right? Because we want our commands to behave like everyone else. I was a little worried about this, but then I did my homework and proved that it's fine. So we're going to turn our naive shouter into a not naive shouter. We're going to make our shouter behave like cat. So the file is pbs150c-shouter. So in order to prove that the minus convention doesn't muck up get-ups, I'm going to add an extra flag here called minus B for ended with a bang. Bang is another name for exclamation point. So if you say shouter minus B, it means turn it to uppercase and then shove an exclamation point on the end. So the minus B just means shove an exclamation point on the end. I'm hoping I haven't lost you yet, although... No, no. Good, good, good. So our shouter is going to take its input from wherever it finds it and then if there's a minus B, it'll stick a bang on the end and it will always convert the input to uppercase. So we start a script by saying bang equals a blank string and then we say while get-ups colon B into the variable opt. So colon means I'll handle the errors and the only thing I'm interested in is the flag B, just colon B. So we have our case opt B, bang becomes equal exclamation point, question mark, we echo at our usage and then we do the shift dance to make sure that if we did pass a minus B, it's not getting in the way of things. That means that whatever came after the minus B is now dollar one if there was a dollar, if there was a minus B. So now we need to figure out what it is we want to shout. So we're starting off by saying the text to shout equals the empty string. So the first thing in my algorithm was if I got no arguments, then I will read from standard in. So if dollar octothorpe or dollar pound sign equals zero, if the number of arguments is zero, then the text plus equals dollar open parents cat close friends. So if I have no arguments, read standard in. Okay. And at this point, text is at this point, text is empty, but you do a plus equals because that's good procedure. Yes. And we're not sure where the text is going to come from, right? It could be coming from standard in or it could come from the arguments, which may or may not be present. So that's why just start with it empty and then add to it at every opportunity. So the next thing we do is we loop over all the remaining arguments, which might be none or it might be infinity. Well, not infinity, but it could be any number greater than none. So for path in dollar at, notice going to loop over all the remaining arguments. So the first thing we say is, is the path the special string minus on its own? If the path is the special string minus on its own, read standard in text plus equals dollar friends cat close friends. Else, go read the actual file. Okay. What actual file? Whatever is passed through the argument. We're going to pass that file? Yes. Okay. Okay. So the command. Why are we doing this text plus equals cat twice? We just did it. Only if there's no arguments. Okay. But now we're seeing there are arguments. Yeah. And that, and if one of those arguments is the minus. Yeah. Then we're going to text plus equals dollar parentheses cat read from standard in. Okay. Yes. Otherwise. Else. Read the file that they passed as the argument. Okay. So minus is just a placeholder for read standard in. Okay. Then we convert to uppercase and we print it out with the bang on the end if needed. So let's see this in action. So remember that no arguments means we assume standard in. So if we say echo hello world piped to dollar pbs 150c shouter. That means pbs 150c shouter has no arguments. So it will read from standard in, which is echo hello world. So it will shout hello world. Okay. If we do exactly the same thing with the minus B, it still has no arguments because the shift has made it so that anything after the options is gone, or sorry, the options are gone, right? That's the whole point of the shift is to make the minus B go away. It will still see, oh, I have no arguments. So it will still read from standard in. So it will still shout hello world. But this time with an exclamation point, children, the end. Okay. Even though we got rid of the minus B, it knows the minus B assigns bang to the exclamation point. And then minus B stops existing, but bang still exists. Yeah, because the shift is after the loop, right? So after we've done our get-offs, we shift. At which point it says, how many arguments have I left? None. Ah, then I must go read standard in. So it does. Okay. Okay. So now let's look at what happens if we just pass it a file. Okay. So, yeah, in the show notes, I still have the echoes there. Imagine those don't exist and delete those from these next two examples. So it's just dot slash name of script menu dot txt. All right. Sorry. My bad. Show notes mixed on my end. So for a second example, it's just name of script space menu dot txt. Okay. So we didn't give it a minus and yet it knew to use menu dot txt. Okay. So have a look at the code. We say if the path is equal to minus we read from standard in, else we read from the path. So we have explicitly said if I give you a file, read the file. That's what the else is. It passes a word you made up, right? Correct. That could be pancakes. Correct. Okay. So you said for path in, looping through the arguments, it's putting the arguments into path. Correct. And so does menu dot txt equal minus? No. Therefore, the else is what happens. The else is read me the file in dollar path, cap dollar path. Okay. So if I give you a file, read the file. Okay. Hopefully the minus will come into play sometime soon. I thought that we had to give it the minus in order to get the file, but apparently I'm misunderstanding. So keep going on your examples. Right. So yeah, exactly. You're, you've got it inverted. Yeah. So we can do the same with the minus b. So we can say name a script minus b menu dot txt. And it will give us the menu with an exclamation point shot at the end. It doesn't. So I did dot slash pbs150c dash shouter dot sh space minus b space menu dot txt. And it looks just like it did without the minus b. Definitely doesn't copy and pasted. Definitely doesn't have the exclamation point on the end. I copied and pasted except for skipping the echo that wasn't supposed to be there. Okay. But the output definitely doesn't have the exclamation points. I was pretty sure I debugged this. Oh, the very last line does. Right. There it is. Yeah. Yeah. Cause it just happens once, right? Okay. Just at the end. Phew. Okay. Gotcha. Okay. Okay. Right. Okay. I expected every one of them to have it, but it's all one file and it's at the end. Got it. Okay. All right. Okay. So now our last example. This is the one where hopefully everything comes roaring together and pennies drop. So we have echo minus e two blank lines. Hello world. We pipe that to the name of our script. So our script now has as its standard in new line, new line. Hello world. We say minus b for a shove a bang on the end space menu dot txt space minus. So that's that's supposed to do. Well, let's look at the code. So if he steps through the code, we do the bang thing and then we do the shift thing. So what's left when we finish the shift is two arguments menu dot txt as the first argument and minus as the second argument. Do you agree? I'm looking at what we're going to type on the command line, but not following where I am in the code. Okay. Well, given that command line, if he steps through the code, the first thing we do is our get ups, which means we find the b. So bang becomes equal to the exclamation point symbol. Then we get to the shift statement, which is going to make the minus b disappear. So at this point in time, dollar at is equal to menu dot txt and minus. So it has we have two are those are two separate arguments. Correct. Okay. Then we arrive at a for loop for path in dollar at So the first time we go through the loop, the path is equal to menu dot txt. So we get to the if statement is path equal to the minus sign. It is not it is equal to menu dot txt. So we jump to the else and we cat menu dot txt into our text. So our text now contains the menu. We go around the loop again because there's still something left. This time we say, is the path equal to the minus symbol? Why? Yes, it is. So we read standard in and we append it to the text. What's in standard in? New line, new line. Hello world because we pipe. Oh, right. Oh, geez. Really? Okay. Well, the part that we were going. Okay. But the pipe symbol takes the output of the echo command and turns it into standard in. So standard in is whatever we sent to the pipe, which is slash n slash n. Hello world. So that now gets appended to our text. Our loop is now finished because there are no more arguments, right? We've done menu dot txt. We've done minus. So we're now finished the loop. So we convert our text to uppercase. We echo the text with its bang. So the output will be our menu followed by hello world. Exhumation point. It definitely does do that. Okay. Okay. So the stuff on the left of the pipe is standard in. Yes. Because remember, that's the pipe's job in life, is take whatever is on my, the output of the command on my left becomes the input to the command on my right. So standard in is the output of that command. I think what's bothering me and maybe this is part of the penny dropping is that the pipe can just, or this whole command can just hold that. I've got standard in. I'm just going to go over here and I'm going to do some stuff. I'm going to go through a loop. And I'm going to say, okay, I've got an argument. Well, I've got a flag. Am I right? Yeah. Get the flag. I'm going to put it into a variable called bang. And then I'm going to say, I'm going to shift. So I don't know what that is anymore. But the next thing I come across is this menu.txt. And that's a file. So I'm going through this if path, this if thing. And it says, is it a minus? No. Well, then just cat the path, cat that file in. So I pull that file in. I'm still holding. Yeah. Nobody can see this but part. I've still got my left hand in the air. I'm still holding that standard in here. Now it gets to the minus and it says, oh, do you got any standard in? I go, why? Yes, I do. Here it is in my left hand. And now it can execute that piece of it. Exactly. It's a stream. So you get to sit from it on demand. Yeah. But you can only have one thing from standard in, right? You can't hold two things. Can it? What depends. If you read from standard in with the read command, it will take it one line at a time. Whereas if you read with the cat, it'll say, sir. Slurp. Okay, right, right. At that point it's empty, right? You've just slurped it. Okay. I think I finally get it. I definitely won't know what it is tomorrow. But as of this instant, I definitely knew it. So I could listen again and my explanation with my hand in the air. Yeah. But that is, I mean, that example, that is it. That is the full it. That is full on how real terminal commands like rep and cat work. There is no, I am not holding anything back here. That is the full Monty, whatever the phrase is. Right? That is it. This is now our scripts behaving exactly like true terminal command with our options, with our proper use of standard in, standard error, and standard out. That's it. We now have fully first class citizens on the terminal and we can write them ourselves and make them do whatever we want. So we're very, very close to finished with Bash. The only thing I'm going to do is a little bit of house. Well, no, don't worry. I've enjoyed Bash. So have I. I've enjoyed a lot more than I thought I would actually. So what I want to do to finish the series out is sort of two things. So we definitely want to learn about printf because only knowing about echo is really bloody limiting because printf is very powerful and very useful. So I definitely want to teach you printf. And the other thing I want to do is I want to cement two concepts that we have used, but we haven't. We've done that thing I do where we have implicitly used them, but I haven't actually told you how they really work. And I don't like leaving things like that. I don't like implicit. Let's be explicit. So there are two terminal concepts we have seen. And therefore when I explain them, there won't be mysteries. It will be gelling. So they're the concept of expansions. And then once we know the expansions, then I can explain the difference between one parense, two parense, curly parense or square parense. Because they have really specific meanings and they determine how the expansions happen. So when I tell you what the expansions are, then I can say, if you use these brackets, these expansions are in play. If you use these brackets, those expansions are in play. And it will actually, it's still, it's still bash, which means it's still a bit head-herty. It still looks like a soup of characters, but there is really solid logic to this soup of characters. But we, if I'd started there, I think I would have lost you forever. So we're going to circle back to formalizing it now that we've seen so much of this in action. And now I'm hoping when we circle back, it'll be a whole bunch of pennies just clattering to the floor. Right? Because we've already seen all the pieces and now we're going to zoom back. And instead of being lots of individual pieces, they're going to make a picture. They go, oh, so someone actually designed this. This isn't an accident. There's actually a structure to this. So I hope that's how it'll end. So that is, that is how we're going to finish this series off. Good. Do we get any homework for this week? We do. So we have our menu script sitting there from last time. And our menu is always coming from the text file menu.txt. Wouldn't it be nice if we could have it come from somewhere else? I would like you to add an optional argument named minus m for menu. If that option is missing, if you don't pass a minus m, always read from dot slash menu dot txt. So unless you say minus m, we're going to default to menu dot txt. If there is a minus m, then I want you to ask two questions. Is the minus m a minus? So if you say minus m space minus, I want you to read standard in. If you say minus m space a file name, I want you to read the content of the file and use it as the menu. Oh, very good. Very good. Okay. So I'm hoping that ties it all together nicely. So we're making use of our standard and so forth. I definitely got to work my way through that. By the way, I've been really good about putting my homework up online. Ed Holland has, I've seen Dorothy putting stuff up. I'm trying to remember to put yours up for you, Bart. Since anybody can do anything, I figure I've got the solutions. I can put them up there. If you ever want to do it on your own, that would be lovely. But I have been trying to remember to do them. I think I'm a couple of behind on that. But I hope I really encourage people to join the PBS student organization. And if you don't know what I'm talking about, that's on GitHub. And all you have to do is say, hey, Allison, let me in and send me your GitHub handle. That's all you have to do. You know how to find me? I'm at Allison at podfeet.com. So that's the best way to do it. And then we talk about it over in our Slack at podfeet.com slash Slack in the PBS channel. I have just added one more line to the challenge. Make a conscious choice about what goes to standard error and standard out. Nuts. I talked too long in the teacher thought of another homework. Yeah, I really should have put that in from the start. So there we go. That's now been added to the show notes and to the magic of gate and on. I'll ever know that was a late edition. Unless they listen to the show. True. All right, Bart. Well, this was a little harder for me than usual. But I'm sure it probably made sense to everybody else. But that's my job is to be confused, right? Well, put it to this, Allison. You just climbed Mount Everest. This is it. This is the peak of Bash. And you have climbed and made it to the top. And it was a struggle to make it to the top of Everest. But you know something? You're at the top of Everest. Oh, good. Oh, good. I feel good about that then. Do. Absolutely do. And hopefully the homework will cement it all in and then it'll be in a much better place. Or you'll be all in that community, all together helping each other climb up the top of Mount Everest, right? podv.com, forward slash like. Right. On that note, folks, remember, 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 let's-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 stay subscribed.