No, let's go on.
Assuming no comments. Slide 25, block scope in loops, a code example.
The only thing I was going to say is the example that you gave would be considered bad programming practices. But at the same time.
That's an individual choice.
Yes. I, again, am using this for purely pedagogical reasons, purely just to illustrate the points.
You're right. I use iterator variables. In loops. I don't use iterator variables outside of loops.
So, but, you know, that's But, you know, there could be a situation where you have a counter And you call it counter and then use the counter inside the loop or whatever and you you find out something different.
Now, this next slide that we're looking at is pretty much the exact same logic or we thought it was going to be the same logic is what we did in the C example, where we're getting a quite different outcome.
So same thing, set I, set it 200, print it out.
Set up a loop from zero to nine. That's what those little dots are there for. Zero dot dot nine.
And then, you know, print out the variable value i inside the loop and then inside the loop At the end, we print out the value of i again And guess what? It's nine.
What happened to 100? That's because that's because In Bash.
Does not obey block scopes, at least within loops. There are cases where it does, but not in loops and also not in if statements.
Now, I did a little bit of research on this. Why is that? Why does it operate a little differently than other languages?
And I really couldn't come up with an answer. If somebody knows, that's great.
But what I came up with research that I did do kind of led me to the um conclusion that the reason is is that the reason is BASH is primarily I shall, right? It's job in life is to execute programs on behalf of the user
You know, the fact that it's a full-on programming language, right, is a little secondary to its its main mission.
I think it would be… less beneficial if variables could be changed and didn't operate inside the loop.
As they do outside. So I think… That could be why, but I'm not an expert in this If anybody has any better answers.
Let me know.
The only thing I can tell you is it would force you to use unique variables within your loop if you don't want the global variable to change.
Right, exactly. And again, that's why I think that better practice, if not best practice.
Is to use i use i j and k for iterator variables, and I never hardly ever use those outside of the context.
Of a for loop. Or even a while loop, right? If I have a calendar loop.
And there, whatever, I hardly ever bother with variable outside the loop having the same name.
Just not good practice. But, you know, that's me. Just think people should be aware of the fact that That could happen. You could get unexpected results.
Does anybody else have any other questions about this?
No other comments or questions. We'll go on to slide 30, variables in functions.
Okay. You can see what we've written on here on this slide.
I'd like to compare bash barrier or excuse me, bash functions I guess stop saying variables.
Dash functions. I like to compare them to you know if you remember basic Basic had go subs.
Right. And it kind of looked to me a little bit like go subs in uh and basic. When you have a function in bash It's almost like you've got… a bit of code, you put a wrapper around it, gave it a name
Then when you call that code. It just kind of copies and pastes it back into the call site.
And continues on. It's kind of like all the variables are like the variables and any other state or whatever right kind of bleeds right through to function into the function and out of the function again.
So we'll see an example of that and also an example of how to modify that.
On the next couple of slips. Again, any questions?
This is probably self-explanatory. We should see more on the code slide.
Coming up.
Slide 33, session scope and function bodies. Code example.
Okay. All right. Now… I want to say something here about code slides that I haven't previously said.
Sometimes, not on this slide. We're going to see it in an upcoming slide.
We'll need to look at a file, either a file that is being sourced from the original script or a file that we're grepping some values out of or something like that.
And so the convention that I used here that if If we needed an external file script.
And I should point this out too. When I first wrote these markdown files.
Turns out that since Markdown uses hash symbols or heading statements, those work as bash statements So I just wrote them and they were just runnable bash scripts. They're not anymore because they're not anymore They didn't look that nice when they didn't.
When we formatted them in formatted them in html But they were at one time runnable bash scripts. You could just type bash And the name of the markdown file And it executed all the code that was in that file.
So the convention that I used was I took the first three numbers.
Of the file and made a directory So in this case, it would be like 3033.
If there was such a directory for this, there would be a directory called 033.
And any files would be in there. So I'm just pointing that out Now, uh.
This… thing here just shows what I said.
Pretty much on the last slide. Which was that create a variable and then create a variable We created a function up above that had that same variable name in it.
We printed it out inside the function. We also changed the value of the variable inside the function So when we call the function and we print the variable back out again.
We see that the function actually change the variable.
So again. These variable names, at least in the default configuration.
Dash functions do not use local variables.
The variables that it's talking to. Are uh now i think that's sort of convenient. I guess that might be One of the trade-offs that they came up with It could be like, let's say for example you want to change.
The path environment variable and you want to do that inside of a side of a function. Well, it would be pretty hard to do that if every variable inside the function was local to that function.
So I think they went with the other approach, which is Let's just assume that global variables or session scope variables are global everywhere, including inside functions.
And they're modifiable within the functions. So the functions kind of act like little mini shells that you're running inside of your main shell script.
Anybody have a question about that before we go on to understanding we can get local variable scopes.
And function.
No comments or suggestions. We'll move on to slide 35. Local scope in functions.
Another code example.
Okay. Now… This is kind of the same idea that we just had.
Except that We've done a little bit thing and and you need to use eagle eyes here look inside the body of the function.
And you should see a local keyword there, right? And… Even though we have the same function that is named, I think it's named bar one, I can't remember off the top of my head.
But anyway. That function, which is session scoped outside the function is also local inside this function.
And we use the local keyword to market as such.
So you don't put the dollar sign in front of the variable name there.
You just use the bare variable name. You can also say local var equals and then some value.
One way to get access to that global variable is to say that local VAR1 equals dollar VAR1.
That'll bring that global variable in there. And you'll have this local copy.
What you can change to your heart's delight And it won't affect the global variable.
One of the things about local variables and functions.
Is that any other function that has another local variable with the same name in it.
Right. Won't be interfered with by this function that has This function calls another function or another function you call two functions back to back.
You're not going to interfere with each other because they're entirely local to uh to themselves and to themselves and This also opens up the possibility that you can write recursive functions in Bash.
I have written recursive functions in Bash. It takes a little bit of care.
And discipline. But it is possible to do. So we're going to see a little bit more about that on the next slide.
But before I go, are there any questions? Sure.
Yeah, I've got one for you. So you're saying inside the function you where you have declared local var one I'm saying you can access the global or session scope.
They're one by putting dollar sign open curly brace of R1, close curly brace.
No, what I was trying to say there is If I could get over to the chat, I would type it in the chat.
But basically, I'll try to say this out loud. If I wanted to pull in the value of the outer scope VAR1, the one in the session scope.
Right. Inside the body of this function. I would say local VAR1, no spaces, right?
Mm-hmm.
Local var one equals dollar sign VAR1.
It's it's yeah
Okay, sorry. Thank you. I mistook your statement as you could use the local one and there was also a way to reference the session one with slightly different syntax. I didn't catch you were setting local to the session value.
Correct. Right. Exactly. And once that's done. The session value isn't there. Now, remember, if you had a different variable name, let's say you called it bar two.
So you said local bar too right inside the body of the function.
Right.
You know the bar one still bleeds through into there. Because, you know, it's still a session scope and it's still You haven't changed it or anything.
So it's just that, you know, it's just sort of good practice though to use the local keyword if you want local variables.
And to make them all You can put many of them on this line.
You don't have to have a local keyword in front of a whole bunch of them.
So if you got four or five variables in this thing, you say, you know, local A1, A2, A3, A4.
Whatever. Oh.
So if you think of how bash. It's not so much a programming language is just running some commands, right?
Mm-hmm.
The local is sort of a built-in command that that creates that far one in the internal scoop.
Mm-hmm.
So before it's created that VAR1, it can still access the var one the parents for our one so that's why local var one equals dollar var one would work.
Exactly. Yep.
Yeah, I get that. No, my question was centered around if you have created the local I mistook it as was there a syntax to get the session despite.
And yeah. And as far as I know, no, but I could be wrong.
Yeah, I wasn't aware of one either. So.
I don't think there's a way.
Okay. Any other questions or can we move on?
No comments or questions. Next slide is 37. Magic variables. Another code example here.
Okay. Now this, I'm taking a little poetic license here. By calling it magic variables.
I've seen them elsewhere described as pseudo variables. I don't like that term because they're not fake variables. They're real variables.
It's just that… they just sort of poof into existence you know, when you need them.
So… Turns out that the dollar sign one, dollar sign two, these sort of positional parameters Act both for… The actual command line arguments to your script Those are dollar one dollar two dollar three also dollar at, dollar star.
Which is just our ad is a list of them. But within the body of a function They are the parameters to the function.
So the two don't mix. If you have command line parameters on your script as a whole And you have local variables to or excuse me, local parameters to a function inside your script You know, the dollar one inside the body of the function
Is the value that's coming in when you call the function.
The dollar one outside the body of the function is the dollar one or the first parameter to the script when you call the script.
So there's a distinction there. By default, these dollar one, dollar two at all are Oh, I shouldn't have said at all, etcetera.
Are all local to They're just like you don't need to put the local keyword in front of them They are local. So again, if you're using writing a recursive function write the current value of dollar one value will be whatever iteration
Of the recursion that you're running. Same thing is true.
One function calls another function And that also has parameters those two dollar ones or dollar twos don't interfere.
So they are local to the thing. And this script just illustrates that.
Or this uh this slide just So any questions about that?
Sure.
I have a question about it. Okay, so, well, first of all, let me just say that there's… dollar sign one, dollar sign two, dollar sign three, passing in parameters.
Like this is exactly like the way DOS batch files worked.
The question that I have here is Assuming that they get changed within this function.
Okay, do… They have… a global…
Do they get passed out globally if that function is used within another routine or Is it strictly values that are passed within the function And only within the function.
That's my understanding. I think that they are, when I ran the test i had one function call another function within it.
Right. And the dollars, the different values of dollar one were different.
So it never bled through and never escapes the body of the function.
But they act, I believe, almost identically to if you put a local in front of a variable That's my understanding. Now, there might be a case that I'm not aware of.
Where what you said could happen. But I don't think so.
I mean, you can manipulate them within that function And pass them out.
Yes.
As part of the…
Well, wait a minute. Hold on a second. How exactly would you pass them out?
I don't have an answer for that. I don't know if that is through as a result of printing the results.
Yes.
Or if there's another option, I don't know.
But that's what I'm, that I mean.
There's no way to get the variable out, but you can get the value of the variable out. Again, if you Yeah, if you echo it, you send it out to standard out You know, it'll go out that way.
If the variable itself is a parameter, let's call it, is an integer And it's in the range from 0 to 255.
Then you can return it from that function. That's kind of limited. I mean, you only have a single bite.
So you can't you can't use Can't use it for integer math you know beyond Yeah, 255.
You could do it that way. You could return it. So… Those are the only ways that I know of.
To get the value, not the parameter itself, but the value of that parameter.
Yeah, that makes sense. So basically… Okay.
Outside of the function. Now, wait, hold on a second. There's one other thing you could possibly do.
You'd have to arrange this in advance. You could assign a global session variable or some other name, not dollar one name Right. Call it, I don't know, global war Right. And you could put that someplace And you could set it inside the body of the pumpkin.
As long as you didn't put the local keyword in front of it.
You could set that equal to dollar one And then the value would be assigned to the global variable When the function exited, that value would be in that global variable. So that's the other option.
That I know of.
Yeah, I make…
I think that's where you would use the global keyword within the function itself.
Yes.
Is there a global keyword? I didn't know there was.
So you could say global I'm pretty sure. It's not used very often because it's For weird situations like that where you want to like assign a global variable that doesn't already exist.
Hmm. Okay. All right. That was something I… should have found out about and should have incorporated in the slides. Thank you, Craig.
Would not recommend using it, though, really. I mean, you should define it before you use it outside the The scope.
Yeah. Yeah, to have them just sort of magically appear out of the body of a function.
It's kind of weird.
I think I saw somebody had their hand up and I also had a quick comment for myself.
Go right ahead.
Not precise, but I have always been under the assumption or understanding that positional parameters were we're local to where they existed.
So your dollar sign one, two, three being positionals are inherently.
Yes, they're positional. And if they are In the outer scope, you know, the main the mains bass script that you've written right they are the command line parameters when you invoke the bash script.
Yeah.
That's $1, $1, $2 train. Inside the, excuse me, when you call a function right It by definition gets dollar one dollar two dollar three you know up to whatever number of parameters that you've done. And, you know, it's also something else to keep in mind
That if you expect that if you expect three variables, but you only pass it two Well, you're going to get three variables.
But dollar three is going to be set to the empty string.
Right. It's a… It always is always the number of parameters that you physically pass to that function when you call it.
I hope that clarified that up. But what's the other question about the other question on the chat.
Oh, I see. Okay. Okay.
It's not in the chat. I thought I saw somebody had their hand raised as a
That was me. That was me, Artie.
I have a question then if you can nest function is there a certain level deep that it just then blows up and is that when it was created.
A long way to override it. You pick up more memory.
15.
Yes. Yeah.
Or are you talking about like How deep can you nest functions like you know to avoid stack overflow situations yeah um i didn't research that part of this talk so I don't really know. I have… inadvertently one time written a bad recursive function
That went into an infinite loop and it did it did stack over But I don't know how many iterations of them do it.
I think it has to do with like how much memory you have i don't know if it I don't think it uses the same kind of stack.
That a compiled program is. I think it has its own, but I could be wrong.
Say that again.
Michelle eventually has written in fee anyway. Your show, or at least most of the ones we use are written in C anyway, so it would probably
That's true. That's true. But I do believe I could, again, I could be wrong. I'm going on Historical information here about the Bourne show.
So I don't know how this translates to badge. But the Born show Um… It used the heap it you it used malik and free to store all of its variables.
At least in the initial iteration. I got that from listening to uh listening to Dr. Bourne give a presentation on the development of the shell.
So, yeah, he said that he said He had some real difficulties with the way that malick and free So he wrote his own. But they did use the heap for that.
Randy, do you want to mention your comments.
Audibly.
Well, basically, as we're talking, I'm also looking up the questions that I'm hearing.
And so basically you're asking about the recursion and so the second or the last comment that I put in there is that it depends on your stack size And so that's kind of, you know, it's as an example, a factorial of 500 might crash
Your session depending on how memory is managed.
Is that an option you can… is set in bash like you can there's a bunch of options you can set in bash And I don't know if stack size is small.
Yeah, I think it's there, but not having done it, I can't say for sure.
Okay.
I'll say if you're wanting to do recursion, there is probably a good chance you should have it already considered another language.
Even if born shell is your choice, you have made an informed choice by them.
Yeah, I agree too. I don't think… writing recursion is i think i think it's fine if you're doing like quite simple things, right? When I used recursion in bash What I did was I had a situation where I didn't have access to the which command.
So I wrote it in Bash. Right. And I just wrote it recursively.
I use the IFS variable, which is the input field separator.
Use that as a keyword argument. And then I just looped through it, just kept testing to see whether or not the value was the value the value of dollar sign path or whatever. I'm sorry, I think I localized it.
The value of that local copy right had become an empty string.
If it did, then I exited with it you know uh non-zero hexico Otherwise, I found it.
Created the um the path. With the current value and printed that out the standard out.
So, I mean, it was very simple because your dollars aren't bad.
Isn't going to be like more than, I don't know. 10, 15, you know.
Directory statement.
So… That would go on or
No further questions. We'll move to the next slide. 40 variable expansions in aliases.
Okay. This is a little bit off the beaten track when it comes to scopes. However, we're going to see this in an upcoming slide.
I thought I'd use aliases as a proxy to describe how variables are expanded within strains and so an alias, when you declare it in bash By the way, I have to point out that It's really difficult.
Or rather frowned upon, I should say, is probably the better term to use aliases inside of a sales rep.
But I'm just using it here so that we can play around with it.
Um… When you define an alias, right?
That's one time you you touch the alias. When you invoke an alias is another time when you touch the alias.
Well, when you invoke the alias, the string that you pass back in the definition gets fully expanded.
So it matters a lot. As to whether or not you use single quoted strings or double quoted strings.
When you define the alias. And we're going to see examples of that in the next I think.
Three slides. So if we can move on, that'd be great.
But if anybody has any questions about that specific topic.
Let me know. Because we're going to talk about it in more detail next.
Going to the next slide, 41, aliases in shell scripts. Another code example here.
Yep. This is a kind of a dumb dumb code example I just did it to illustrate two important things.
One is that if you do have a shell script. And you want to expand the aliases within that shell script.
You need to put this SHOPT line in there.
It's the SHPT dash s that means dash s This is like a Boolean flag.
Right. And there's a Boolean flag called expand underscore aliases.
And if you set it, it turns it to true. If you use the dash you use the dash it unsets it.
So if you want to use aliases. Inside your sales script, you're free to do that.
It's not good practice. Because you might have… like an alias you invite alias ls to something and you know it's going to expand the alias before it calls the actual command.
So you probably don't want to use aliases unless you have a real good reason to do so.
But I'm just using it for this. In this particular slide, all it does is just sets the That's the thing and it says, okay, wow, hey, what's the current process ID of this running script.
And so we can print it out there. But we're using a single quoted string to do that.
So that's the only reason for this slide. If any has questions about the the shop line, let me know.
Otherwise, we'll go on to the real meat of the slide, which is the next slide.
Going on to the next slide, 43, aliases are just string expansion.
Another code example here.
Right. I think we said… Yeah, this slide here.
The title of this slide. Is a little misleading. It did have some code on there.
But all we're doing here And I mentioned this earlier, is that sometimes we are going to use a file In upcoming slides.
And this just cats that file So you can see what the values are in the file.
If this was a live coding example, and please don't ever ask me to do that.
A live coding example. I would just cat the file and say, here's the stuff in the file And then we go on with the next bid.
But you can see there's some some names and some some statements in there and stuff like that. This is a random a random list of blinds.
So we can go directly on to the next slide.
Next slide, 45. Double quoted strings get expanded before the alias is defined.
This is another code example.
Right, exactly. Here's what I was talking about back a couple of slides ago.
So a double coated string versus a single quoted string There is an order there is an order to evaluation in bash certain things happen before other things happen.
So… I have a link And it will be in the references which will be in the downloadable Dowerball.
Which will be up on the website soon. To complete list of the complete order of evaluation.
Of every possible condition in bass. One of the things that happens in this case is that strings within either embedded variables or embedded sub-shell expansions, aka command substitutions.
Right. Those get expanded. Before the alias is even defined.
So we're not even defining the alias yet. We're doing the thing on the right-hand side.
And that's going to get expanded first. So that means in this code example.
If we've got a variable And it has some value.
And we define this alias. And then we put it inside a double quoted string.
Well, the value of that variable is going to be permanently affixed to that.
And so we either re-alias it or do something else. But changing the value of the variable will have no effect on the aliens.
And I hope that is clear from the slide.
We're going to see how to change that on the next
Going on to the next slide. 48. Only single quoted strings preserve the current value of the variable.
Okay.
This is another code example page.
Right. And I think this is very similar to the last code example Except that we're using a single coded string.
And in using the single quoted string right It's It's just locked in.
It's locked in. The dollar sign is locked in. Everything is locked in nothing exists nothing is evaluated.
On the single quoted string. Is now assigned to the alias.
When you expand the alias. At that moment in time The alias gets expanded first in this situation.
Spring gets flat out. Then any other evaluations that need to happen will happen.
So in this case, the dollar sign variable will get expanded and its current value will be settled.
So regardless of what it was set to before you define the alias.
It will now take on the value of the current variable.
I should mention just i should mention You know, for completeness sake.
That in the previous example. If you wanted the same behavior you could escape the dollar sign.
You could have put the variable in the double quoted string and put a backslash and put the dollar sign.
And you would get the same behavior as what's happening here.
So, any questions about that?
Okay.
No.
Going on the next slide, 50. Sub shells.
Yeah. I should mention that. As we go along here, this is getting deeper and deeper and deeper.
So, hold on a second.
All right. Now… I'm going to ask a question here. I think everybody already knows the answer to this question.
But I need to know because I need to explain some things And I don't want to proceed.
Unless everybody is comfortable with what I'm talking about.
So as everybody understands The ORC system call and the exec system call in Unix and Linux. What they do and how they operate.
If somebody doesn't know that. Either raise your hand or raise your hand put something in the chat or something Unmute your mic.
I went ahead.
Okay, I'll yell out loud.
Which is what?
Ed, I was going to say, why don't you go ahead and give a brief overview if even if not for the people joining this evening for the people watching the recording in the future who might not know.
That's a very good idea. Hold on a second here. I'm migrating over to my second glass of water. I've consumed my first glass of water And I'm going to go over to my second.
Apologize for delaying here. I need to… quote unquote wet my whistle.
Before I do so, but I will go ahead and do so. This is going to be a very, very brief high-level overview.
Okay.
Interest my ear there. Okay, so in Unix, and this applies to Linux as well.
Works on the concept of separate tasks being separate process. Right. And it's possible or one process.
To make a clone of his cell phone And that would become a child process.
Our first process was the parent process. And the second process is the child process.
Now, it does so by calling the fork system call.
The fork system call has a unique property in that It does a clone.
All the data, and I'm eliding a lot of stuff here So, you know, don't uh don't hurt me on the missing out on the details.
But it gets a complete copy of the code and data for that process in another region of memory.
The effect of that in bash is that all the variables, all the runtime environment, let's say the current directory The file handles, standard in, standard out, standard error.
Those are all copied over to the child process. The next thing that could happen And this is pretty much what the show's job is, is the exec system call.
The exec system call takes a path It takes a little bit more than that, but I won't go into detail there.
But essentially, it takes a path to some program.
And… it will read that file off of disk from that path and overlay that that child processes uh doesn't have to be the child process But it will overlay that processes memory of data and stuff.
Okay, so that's extremely important to know. So when you're in the shell and you type LS, which is an external program First thing that happens, fork system call gets called.
We now have two batches. Dash number two, which is the child process.
Then calls exec with slash bin slash ls And LS gets read in from disk overrides, overwrites the child process, which was bashed, it's now ls LS starts to execute It has all of the environment variables, but doesn't have any of the bash variables.
I'm going to get to that in a minute. And it just continues on its way.
The original bash process gets the PID so that it has access to job control and other things.
For that trial process. Can also do things like connect programs together using the pipe.
Operator. Waits for the process to exit. Once that happens.
It takes the Exit code from that process.
And stores it in a special bash variable called dollar sign question mark.
I'm sorry that was really fast. Really overly simplified overly But that's what you need to know from the next couple of slides.
Are there any questions about that?
I would just say that based on what you just said.
Is that it's a single tasking oriented process. So one task starts and up And when that second task ends.
It goes back to the first task. And then it continues.
Yes, you are right about that. However, there's a There's a slight wrinkle.
And I was just really interesting when I found out about this.
There is a… a set of YouTube videos.
And I can't remember the channel that it's on or whatever.
Where they did an exploration of xv6 XV6.
Is a modern C compiler implementation of these six units.
V6 Unix was the version of unix that existed before The Bourne show was created.
The Bornshell was created and it was implemented in V7 Unix.
Which was in 1979. Xv6, however.
Was the most recent show prior to that. And there's a YouTube video that talks just about that. And he went into great detail By the way, it's all up on GitHub. You can download the source code.
Read it for yourself, compile it for yourself play around with it with your spider style.
Interesting thing about what you just said is the following. And that is that normally is that normally Bash does one more thing.
In addition to what I just said. In the parent process.
Having received the PID, the process ID of the child process.
As the return value from the four system call.
It next does. A weight system call.
On that thing. If the bash process as seen a single ampersand trailing the command line that you've you've entered there, right? It does not do the wait system call This means that the child process Now we'll start running and it will be running in the background.
You have access to job control. So if you say jobs It'll list them. You can foreground that job.
You can kill that job, you can background that job. You can look at its status.
All that sorts of things.
By the way, just as an example, I don't have this in the slide.
But if the Child processes bash that you're running You have access to the parent process id too Normally trial processes don't have access to that.
But in the case of BASH, If you do dollar P-P-I-D, all uppercase.
That'll give you the the process ID of the parent process.
But no, it can be multitasking in the sense that it can spawn background for our processes.
As well. And it does that by using one less system.
Sorry for the long-winded. Explanation.
No, but it's a good explanation and that's helpful.
Okay. Should we go on? Bye.
I think so.
Going on to the next slide. 51. Exploring dollar and dollar bash PID.
This is another code example page.
Yeah, we haven't really even gotten into sub shells yet. So we'll start here with this sub-shell.
This is a code example. Um So a sub-shell is a sub- Some commands, a group of commands one or more that are between parentheses.
And you must have These parentheses must be in the command position.
This is the thing I mentioned before. A command position, right?
The moment you create a sub shell. The first thing that happens is that BASH forks itself.
It becomes a trial process. It does not do an exact.
So it doesn't run another copy of Bash. It's just its own cut.
So it has access to all the variables the current directory, standard in, standard out, all the other various things that are in the runtime.
It has access to all of that stuff. All the environment variables, etc.
It's just running in a child process. So… Just check something here.
Right. So it turns out that If you want to know if you want the pids of these various things There's a couple of things you could do. So dollar dollar is the outermost bash process. It's process id It stays the same.
Across these subsystems. I'll get to why I think that is in a minute or what my research has led me to.
To conclude. However, if you want to know if you want what the individual child processes ID is.
It's dollar sign bash it. And if you do that outside of the um of the of the sub-shell.
It will be the same as dollar-dollar. But inside of the sub shell It might even Well, when I was running it.
Obviously, I ran the BASH program on that.
And so the very next thing it did was generate a new process. So it ended up incrementing the pid by one.
But that was just, you know, that situation. It's not normally that situation.
So on a very busy machine, you're probably many many iterations past that.
But bash bid is the sub-show. If you have a sub-show, within a sub-show.
You will have… another bash pit.
And another bad bit and so on and so forth. So the dollar dollar will be the same for all of these.
And like I said, my research led me to conclude that kind of the thought there was… you might, let's say, like Stan mentioned this earlier today you might have decided, what if we had a log file and wanted to
Start writing things to the log file. And we wanted the log file to be unique through our script, not unique to the sub show, but unique to the script.
Well, if we wanted to use dollar-dollar as a way to form a unique file name this would be a way to do it.
So you could start writing the dollar dollar stuff In the outer part of the script.
When you hit the sub shell, dollar-dollar is still the same PID, same value, I should say.
And then you could still use You could still form that log file, although I don't think that's a really great way to do that. If it was me, I'd create a yet another variable that was the name of the path name.
Of the log file and I just use that inside the central.
But that's me. So just so you know, a dollar dollar inside the subjugo.
Is the same for the outer shell and the sub shell. The bash pit is different.
So that's all this slide has to say. Any question? Okay.
I find that i find that a comment. I find that very interesting and almost the inverse of the use case that Stan was talking about.
Hypothetically, if I have some moderate length running sub-processes.
I would prefer that they log to their own log file.
And I habitually have used dollar dollar as a unique log file name component But I would need to use dollar bash PID.
Yeah.
When doing sub shells to achieve the goal. I wanted where I would have a separate log file per subshell.
Yeah, if you're using subshells. If you're using external scripts, however.
Right.
Remember that like if you just say dot slash You know, in some uh some file name that happens to be executable bash script. That will have a completely different dollar.
Because it's a different instance of that. So.
Yes.
Shall we go on?
Ready to move on? I think so. Slide 52, variables and sub shells.
Yet another code example page.
Okay.
Okay. So this is an example of where we finally get to the place where BASH actually does actually kind of obey most of the scoping rules that you see in other programming languages. And that is the fact that the variables, like I said, they are copied over to the sub-shell
And the subshell can change them at will. But it has no effect on the parent value of the variable.
Once it's in the sub show, it's its own unique copy.
And so you can play around with that all at once.
It cannot affect the parent shells. Copy of that same variable name.
That's just another example of like You really can't use a sub shell Right. To manipulate uh let's say a global recession scope variable.
It just doesn't work that way. There are other ways to do it.
But you can't use variables to do it that way. So that's all we're saying on this slide.
Any specific questions about that?
Okay.
Well, I'm going to say that I think, once again, this proves the point of misusing variables what would be considered good practice programming.
Right. Yes. There's good. Then there's the rules of the road, right? So all I'm saying here is that um you know if you know if you're debugging I don't know, some intern shell script or whatever, right? And he says, well, look, I'm changing the variable right there. I'm changing the variable.
Why isn't it changing, right? And you just have to point out to them, well, you know, you're inside of a subshell. That's not that variable. It's not the variable you think it is.
These are not the droids you're looking for. So we're going to see an example that very shortly where that can really lead to some confusion.
And I was not aware of this until I saw a certain video So maybe we can move on. Unless there's any other questions.
Assuming no more questions or comments Slide 55, not a subshell.
Yet another code example page.
Okay. All right. So these next two slides We're going to be talking about Something I'm calling command groups I don't know if there's another term for these.
I might have missed them. I didn't find it in my research.
These are not sub-show. They are just groups of commands.
But they kind of act as one. What I mean by that is… They need to go in the command position.
Like I said, they need to be enclosed within curly braces can have one or more. Typically, you wouldn't want to have just one Because if you just had one command well then you don't really need the curly braces.
But if you have multiple commands in there You can do that. The difficulty here is that This is all running in the same session.
So we're going to see that in the next slide But a couple of tips to remember here.
If you are running this Um… on a single line as opposed to multiple lines multiple lines It's not that big of a deal. You would do an open curly brace.
New line, put some code in there, new line, new line, no more code.
New line and then final curly brace on its own line that works just fine. But if you want to do it all on the same line, you have to remember these tips, which I've I've enlisted here or listed here.
You had to have a space after the initial curling phrase.
You have to have a semicolon. Before the final right curb brace.
So just remember those tips. We're going to stay on the next slide.
A more valid use case of why you might want to do something like this.
But more to the point of how variables are affected, or rather i should say they don't act like they do with subsum.
So any questions here or do you want to move on?
I would just say that I would say that the curly braces usually denotes a routine.
Not necessarily a function or anything else but a routine. Of commands that you've kind of grouped
Yeah, I'll give a brief example. A matter of fact, I think it's on the next slide. Let's go to the next slide.
So we can see what's going on there.
Next slide, 56, command groups Continued this code example.
Yeah, this is have the word true in front of it?
Page.
No.
A dozen okay sorry all right um Yeah, I typically… use a… Go ahead.
Hey, Ed. There is the word true in front of something towards the bottom of the slide.
That's true space double ampersand space.
Oh, it…
Okay, in front of two ampersands. Right. That's what I'm talking about. Yeah, that was a Yeah, I wanted to point out that that was… The idea there was the idea there was True is like a proxy for some other command that you're running, right? And the other command
Could possibly fail, right? Or could possibly succeed.
So… based on the exit code of that command.
Right. You can optionally run more than one on the other side.
When I'm talking about the command position. So we go to the left of the two ampersands.
That right there is the command position And we can put a command group there.
And we can put multiple commands in there. What I typically tend to do, and this is just because I like You know, not a lot of I'll typically, if I've got a situation Or I run a command, and if that command fails, I want to just get out of Dodge right away.
I'll just do the simplest thing I can possibly think of. And it is i'll put two pipe symbols, which is a logical or in this case after that command, followed by curly brace space and then I'll say, I'll have like an echo statement like echo previous command, whatever that is.
Failed and then semicolon And then like exit one and then base semicolon again and right curly brace And I'll do that because I'll do that because It's a nice little common pattern if i want to exit out of the script because if that command that I tried to run
Didn't work. It's a lot nicer than… like wrapping the whole thing in in a multi-line if statement. Multiline if statements for simple things right time tends to clutter my code. I'd rather do it this way.
But that's me. You don't have to do it that way.
But I just wanted to point out that the the variables that get changed within the value or within the curly braces are the same session scope variables as they are outside. That's all running in the same session.
There's not a separate process. If you looked at the bash bid, it'd be the same as the dollar dollar.
There. So. Any other questions about that?
Going on to the next slide. 57. Executing external commands in sub shells.
Okay, let me just double check this one minute. Wait a minute. Doesn't that work?
Something didn't work here.
Should I read the shell to you?
Narrative.
Yeah, I'm sorry about this, guys. My, uh. That particular one kind of died. Yeah, just give me a quick summary of the Other slide. Sorry, guys.
Okay. When a subshell runs one or more external commands, it forks itself again, creating a grandchild process.
Still using…
Right, right, right. Stop, stop, stop, stop. Okay. The reason why I didn't have anything there was because I intended to skip this slide. This slide was in there for Completeness sake.
It has nothing at all to do with variable scopes. So we're just going to skip it, but it's in the slide deck if you want to look at it.
If you want to look at the research material, it's in the references So we could just skip that and go on. But the basic idea is, yeah, it just runs as a branch out process.
So.
Slide 58, subshell functions, another code page example.
Yep. Yep. God, I went past. Where am I supposed to be? I went too far into my… into my speaker notes. But anyway.
Yes, this is just a point to bring up the fact that if you write a function and instead of using curly braces.
Just around the body of the function Instead, you use parentheses that will automatically run dysfunction in a subshell.
It's nice little implications there. First off. All the variables are local to the subshell.
So they won't bleed through to the body of your function.
You don't need to put the local keyword in front of there.
Also, your runtime environment variable has been copied. Excuse me, your runtime environment has been copied, which includes the current directory And if you change the current directory within a sub-shell function.
It will stay within the body of the function. And as soon as the function terminates you'll be back to your original directory.
So that can be very, very handy. You don't have to do crazy tricks like push d and pot d which i have done in the past.
I wish I knew about this a few months ago. I would have definitely used it.
But on the other hand, be aware that it's a little bit less performant Because you are creating a four trial process.
Which is a fairly expensive operation. So keep that in mind.
Any questions about that before we go on to the really nasty bit of code?
No questions or comments. Going to the next slide. 59. Unexpected sub shells and their impact on variables.
This is yet another code page.
Yep. Yes. You know, you have an example. I meant to go clean this up a little bit right but There's a little… function inside of a inside of script that we source. I didn't want that to be the case but
I was running out of slide room here. I just put it in an external file.
But basically, if you looked at that function. It just goes through a loop and it just… does a whole bunch of echoes, right? So you're getting multiple lines we're going to call that function And we're going to pipe it into this wild loop
And this while loop, the predicate in the while loop is this read function. We're going to read the value that's coming in once per line And we're going to store that value in this in this L.
In this L variable. The reason why this is kind of weird is because It doesn't look like it. There's no parentheses here at all.
Yet by doing this, you're literally creating an implicit sub-show. That while loop and that read command all even though they are all internal to bash There's no external stuff going on in there.
That is all creates another trial process. And runs it there. Now, my suspicion as to why that happens is due to the fact the way that the pipe operator operates.
The pipe operator connects the standard out of something standard out of to the standard end of the next thing in the pipe.
Well, if all you had were internal bash things. And you pipe something to itself, is that possible? And you can act your own standard out.
To your own standard end. And I'm not sure that that's even doable.
So the decision was probably made to implicitly create a sub shower.
The implications for variables, on the other hand. Is a little bit rough because that is a sub shell If you were trying to compute something.
Using the stuff that was coming in. You can't really use variables to do it.
Because any variables that you create and or modify inside that sub-shell are going to stay inside that tub shell.
We see an example of it here We try to print the value of the L variable Outside the done statement And lo and behold, it's equal to what it was set to before we even started this entire process.
So that's something to be aware of. If you do something like that.
You can't get access to those variables. You have to use some other mechanism.
And this is our last thing on subshells before we go on to environment variables.
Any questions?
No questions or comments. Next slide, 60, environment variables.
Okay. Sorry, Stan, my little My little access thing here is not working Can you just uh briefly Yeah. Okay. Okay. Okay. That's all I need. Thanks. Just need to get me started there.
Features of Unix process model not specific to the shell but
Right. So originally.
Unix did not have environment variables. But when Dr. Born.
Came over from England. To work at Bell Labs.
He really needed… to be able to pass data across cell scripts.
I think in those days. There wasn't a lot of memory to be had in computer systems.
And so… You know, it's uh it's You don't want to like write one jig big massive shell script. You probably wanted to write bunch of series of smaller jokes.
So Dr. Bourne. Went to talk to Dennis Ritchie one day And said to him, hey, can you put environment variables in the Unix process model?
And he convinced Dennis to do that. And Dennis, in fact, did do that.
So we've had them ever since. And they're in a lot of operating systems.
So they're also in Windows. But they're in Linux as well.
The idea, though, is that you have a parent process.
And it has some number of environment variables that it got from its environment.
And when it creates any child process. That child inherits all of those variables.
There's something unique that happens with bash And I think we're going to see that in the next slide.
But… Even though environment variables seem to be part of BASH or seem to be part of the show they're not really.
However, the shell is a very convenient way to manipulate, create them, to read them and so forth.
So we're going to see more of that as we go along.
Any questions about that before we proceed?
Okay.
Going on the next slide, 62. Child process from Bash.
Okay, let me just double check then.
Okay, you got it.
When a child process is spawned.
Okay.
Okay. I'm sorry. Was there another question?
Let me just clarify. So you're saying that the environment variable is not actually part of the show for the subject
Not technically. The shell is like a tool to manipulate environments.
Does that make sense? So you have environment variables that you inherit.
When Bass starts up, we're going to actually see that on this slide. But when Bash starts up.
It inherits all of its environment variables from its parent process.
So that's whatever that was. You know, it could have been the login show.
A terminal or something like i don't know. So, yeah, it's uh It is not a feature of a show.
Is a feature of all Unix process.
Okay.
Going on to slide 62. Child process and bash.
Okay. Yeah.
Right, right. So one of the things to point out about uh uh bash as a child process.
Not a child process created from BASH. But bash as a child process.
When it starts up, it inherits a parent's environment variables. One of the very first things it does is this import process.
This import process, you can't see that. It all happens before your script even begins to operate.
This import process takes all of the environment variables imports them, creates actual bash from the environment variables.
And we'll see it does another little thing, which we're going to get to in a minute.
But this allows you to have access to various things like dollar path, dollar user, dollar home.
All the various environment variables. That you've come to know and love They're all available as if they were just regular old bash variables.
And you can change them. So any questions about that before we go see some questions about real live stuff.
And I'm wondering if Lee might be on the same vein.
Would it be fair to say… that the environment variables live in the parent process. But when you start bash or similar shells they instantiate a copy.
Of the parent environment variables as variables in the current shell or the current scope.
Yes, I do believe that this happens. By the way, I'm sure that this happens in the exact system called.
I think the exec system call Now, let me take that back a second. No, that's not right. This happens in the fork system.
So when the fork system call starts up. Of the child process gets a copy of its entire process is memory.
But there's a region of that memory which is the tag as the environment variables.
And so if you're starting bash it's just like you have another clone copy of Bash. That's what was happening back when we were talking about subshells.
But if you're going to do an exec Right. And exec another program like LS or GREP or something Um…
The program will come in and overwrite the copy of BASH.
However, the environment variables that it had R.
The same environment variables that that child had. And you're right, Grant.
They are in fact copies of the parent. Environment variables so the child has the ability to modify its own environment variables Which you can pass on to its grandchildren But it cannot modify the parent's environment.
So does that make sense? You can't modify your parent. That you can modify your children.
Yeah, it makes sense to me.
Okay. Makes sense to everybody else?
Does to me. Let's move along.
Okay.
Next slide, 63, the set and print env commands.
Okay. Well, anyway, I'm going to wing this one.
I don't know what's going on with my little teleprompter here. I might have to restart this.
Okay. The set command has a bunch of different functions.
But one of which is if you just run it by itself.
It will print out all of bashed's variables. This is a union.
Of the regular variables and the environment variables. On the other hand, the printenv command excuse me, the print ENV built in Right. Only prints out environment variables.
It's built into bash And you're going to think we're going to see a difference between that And the actual ENV command in an upcoming slide But the print need to be only prints out environment variables. The other thing the print env command will do
Is it will allow you to investigate a given environment variable.
So it's possible using some clever coding Right. To determine if a variable is an environment variable.
There may be some other way to know that. But when I've done it, I've used it I've used like a diff between set and uh and print and v.
To figure out whether or not that variable was an environment variable.
So, I mean, you could have an environment where, let's say, are all lowercase or something and The uppercase thing, by the way, is just a convention.
They don't have to be uppercase, but if they are If there are some lowercase ones or whatever, and you're trying to figure out, is this a built-in or is this a Well, it could be a built-in variable into bash. It could be one that's common to your script.
Or it could be an environment variable. Front A and B is a way to get that.
Any questions about that?
Okay. I should mention just for historical note that there was a difference of opinion between the boys at berkeley And the boys of Bell Labs. When they were doing vsd Especially when Bill Joy was writing the seashell And some people…
I didn't really like to see show but anyway He went with print ENV.
And the people at Bell Labs they went with the external ENV committee.
We're going to see an example of that in the next slide.
Going on to the next slide. 64. The env command.
Okay, so… My first encounter of the NV command, and might be yours as well was in the shebang Enerline to a shell script.
That's like the very first line in a shell script. We're going to be talking about shebang lines in a minute.
But user bin ENV. Was the first time I saw this.
And I didn't see it was bash. I saw it with Ruby.
They were saying, hey, when you want to run Ruby. You should use user bin env Ruby.
I never knew why. I never really investigated it. I just took them at their word.
But… the env command has quite a few more uses.
If you just run it by itself it will print out all of the environment variables In the form of you know name equals value name equals value name equals value One per line. And that's pretty much what the print EMV command does too.
If you just run it by itself. The difference is that threatening v is is internal to the show, whereas env is an external program You have to fork an executive.
To get it to run. However, the NV command has a couple of other little nice little use cases.
One is which we're very familiar with is that if you give it a program name.
It will execute that program in the current path.
We'll see why that matters in a minute. But… The other thing it's possible to do is to change environment variables prior to running the program.
So for example, you could change I don't know Dollar Home or something.
Right. To some other value. And then execute the command.
So you don't really need to do that with bash NASH has keyword parameters, which we'll talk about in a minute.
But it is useful for running things that are in the path.
And I'll give an example of why that's important to me personally.
On an upcoming slide. Any questions about the ENV command as a whole?
It has some other use cases. You can do a man page on it if you want.
Yes, sir. Uh-huh.
I had a comment on it. One of the reasons, and I've started seeing it probably within the last five to 10 years, like the use case you saw with Ruby.
Mm-hmm.
Is you cannot guarantee where a particular command will be.
By convention, ENV is in user bin. So you call that and give it the name of the the actual interpreter you want to run And it will then look through path on your system and find the command on that given system's path.
So you might have something you might have something in a weird directory on Mac OS, you might have something buried under user local blah.
You might have opt something But using Ben Env bash allows ENV to locate bash for you or Ruby.
For you and then use that interpreter to run your code.
I'm sorry.
Yep, yep you've just explained the next slide. I believe. Oh, that's fine. That's fine and and uh I'll just expand on that a little bit when we get to it.
Any other questions?
One more comment. Yeah, one more comment and that is… I know you didn't mention it, but the environment variables are in all caps.
Just…
They can be, but that's just a convention.
Yeah, that's the standard convention that you find actually in DOS, Windows, and also here in In Lennox.
Unix and Linux. Yes, that's true. And I don't really know why that is.
Theory that I've heard propounded. Was that in the old born shell days.
Well, let's back up a bit. Before the Bourne show.
There was the Massey's show. That was done by john mashi.
At Bell Labs. He was in a different group from the group that Richie et al. Were in.
He was in a group. They created a group An addition of Unix, it was called the Programmer's Workbench.
Their version of the show, or rather his version of the show.
Had single letter uppercase variables. That's it.
Is that 26… That's all you could have, 26 birds, right? They were all just one letter.
That one.
That was… part of the issue. Then when they got to the Vorn Show, which basically they say, oh, well, we can have, you know, as long variable names as we want.
The theory that I heard was that when you look at a shell script.
You can see, oh, here's some commands And oh, look at that. There's a uppercase variable shouting at me. Well, that's a variable.
Right. It's not necessarily environment variables It's just a variable, right?
Oh, when I started in this game way back when. We always uppercased all of our environment or excuse me, we have paste all of our variables regardless of whether they were environment variables or not.
And I don't know if that's why I don't know if that's why that convention arose.
Just to sort of distinguish it from I don't know, you know, options on the command line or something like that.
Okay, go ahead.
Okay. Are we ready? Did I lose everybody? Oh, okay. Sorry.
Yeah. Stand fell asleep.
Now we're here. Bye.
65. Shebang header lines.
Okay. All right. Hold on. I got to catch up to this my uh my My thing is, what number is that?
65.
Stan. Ah, okay. Hold on a second.
Right. Okay. So yeah, we were just talking about this, Brian. I think it was Brant, right?
Yeah.
Um that uh the uh The shebang line. So how the shebang line operates, right?
This is an important thing to know. When you are going to call a script.
Things going to happen is the exact system call is going to get called.
The exact system call, one of the first things it does is it loads that script into memory.
Then investigates the very first few bytes, usually the first two bytes and sometimes more.
Of the file. If the situation is.
That it's the ASCII value for the pound sign or the octetor.
And the exclamation point aka the bang. This is where we get the term hash bang.
Or shebang. Then it says, oh, okay, I'm going to take a certain code path Where I'm going to look for a look for an interpreter tomorrow.
Now, back before… We had shebang lines.
The default behavior If it didn't see it.
As a as an actual binary file that was compiled with some compiler, generally the C compiler, right? It assumed it was going to run it as a bash or excuse me as a show script. And it would just launch
Slash bin slash sh. And pass it this file as an executable to run.
But after we got shebang lines implemented You could swap out the interpreter for some other interpreter.
The issue, however, is that exec is not very smart. Or rather, it tries to do something as efficiently as possible.
And one thing you don't want to have it do have it, you know, chug through all the directories in the path looking for the file that matches the closest thing in the path right that matches and is executable and has the right permissions and blah, blah, blah, blah.
That's an awful lot of work for it to do. So what it needs is a well-known path.
A well-known path, something that it knows So you probably have seen lots of shebang lines that start, you know, crunch bang Well, that's fine.
But as Grant was saying earlier. When he stole my thunder. Thank you, Grant.
If that might not be the one you want. And this is where I come into play.
And that is that I was, I'm not not me personally, but I'm just saying how it affects me personally.
Is the fact that since I'm running on Mac OS, My bash is not slash bin slash bash.
Apple and their infinite wisdom, decided to pin the latest release of their bash at the three dot x branch of bash But it's moved on. It's up to version 5 now.
And so I installed the latest version of BASH on all my Mac OS boxes with a home brew. So I just brew install bash And now I have it, but it doesn't live in Bin Bash.
It lives in something like home opt bin or something like that. I can't remember the exact.
Location of it but it's it's in It's in home brews default directory.
And so that's the one I want to run. So whenever I write to shell scripts. I use user bin ENB in my shebang lines and then put bash out there.
That ensures that the bash I get is the bash I want.
That's an important thing to do. Why is it user bin ENV?
Well, again, as I mentioned before. It needs to have a well-known path.
And it always knows that ENV is in user pin. Well, it doesn't know that.
You've told it that, right? Once ENV, well, NV says, oh, okay, you want to run bash? I'll investigate the thing.
Now keep in mind, there's a lot going on there. First you're executing another external program.
You're executing E and V. It's turning through the path, looking for a valid thing that matches its first argument.
And then it runs it and then it runs it defines a bastion, let's say in my case, in home slash op slash blah, blah, blah.
Runs that or whatever. So now we've got a third program running so we uh you know we've we basically uh did basically to execs when we could have gotten away with just a single exec.
So that's another thing to keep in mind. Any questions about that?
I'll just comment that there are still to this day.
Mm-hmm.
Vendor provided unixes that don't necessarily include bash by default. So it's not that it's not in Ben Bash, which is a historical convention it might not be on the system at all. Or if it is, you've squirreled it away in your home directory
Or something else. Env can find it.
Yep. Yeah, you're absolutely right. So I apologize, it wasn't in special brew. That's where they put everything there in Homebrew.
Okay, you want to go on?
Yes. Slide 66, the export command. Another code page example.
Okay. Yes. Now the export command is where the rubber meets the road.
When it comes to BASH and environment Right.
As we mentioned before. When BASH first starts up, it does an implicit import process where it takes all of the environment variables and generate actual bash birds out of them.
Allowing you to manipulate them If you couldn't do that, it would really make it hard.
To use bash or to use any show, especially when it came to environment variables The export commands is a way of taking a a new variable and promoting it to becoming an environment variable.
And when you do that. I believe, and I don't know the internal mechanism of this.
But the experiments that I performed have shown that when you do that, it immediately turns a flag on saying that that is saying that an environment variable.
You could use The dash n flag to export to turn that off explicitly.
But the point about it being turned on is that if you've done it once.
And then later on you change the value of this variable It has changed in the environment.
So all child processes After that change, right, we'll have the new value You don't have to re-export the command. That's the essential thing I'm saying about that.
One final thing to say about the export command. Is that and this is a led to a little bit of a problem.
But there's the dash F command. I don't know if this is unique to Bash.
I don't know if other shelves do this. I haven't investigated that sorry But if you have a function.
And you say export dash f in the function It creates a special environment variable that contains that function.
What that is handy for is if your child process happens to be another bash script.
That math script will now have access to that same function.
Because when that other second bash script starts up.
First thing it does is it imports all of the environment variables.
And creates… variables out of them inside of this little runtime.
But when it sees that it's a function, it says, oh, I'm not going to create a variable here.
I'm going to create a function. And so that's one way you can export a function to another bash script.
Led to a problem in 2014. Aka the infamous.
Shell shock bonus. Don't stop vulnerability.
Was not due to the export command. It was due to this import process.
The import process read that in.
And the parser just kept going. So if you added some nasty code after the body of the function.
It just went ahead and executed it. And if your batch process was running in a privileged environment Let's say as part of the, I don't know, Apache web server And it was launched as a CGI script.
Well, well, well, well. You just took over that box. So want to know more about that? We've got links in the references.
Any questions about the export to me?
Going on to the next… Next slide, 67.
If not, okay.
Env and . Files.
Okay. This is an area that is an area I have limited expertise on.
But my general question here is that You can use these files In order to set environment variables Without… you know kind of like exposing them let's say… you know if you have any secrets like off tokens or passwords or something like that
You can put them in a .env file. Sometimes they're called .var files. I've even seen envrc files.
But how do they work, right? In Bash is a very limited way of making these work.
There are other systems that work a little bit better. I'll talk about that in a minute.
But the way that BASH It's the following. First off, keep in mind that this does not work from the command line.
Your repo will not You can set this environment variable, this bash underscore env environment variable, you can set it to .env But it won't have an effect on the REPL.
It only has effect on bash when it first starts up.
So you have to be running a bash script. To get this to work but that's probably what you're doing anyway, right? You're testing your code So the basic idea here is very simple.
Before you begin, you set this environment to equal to… Some relative path name.
And if you just say dot env. It'll be this hidden file in some current directory. The file doesn't have to exist.
It just has to be in the environment variable. Bash underscore EV.
Then… you run your script.
If there is that file in that directory, any code in there will get executed, usually in the environment variables are set at that point.
So the idea is you'd set that maybe in your own bash RC or something like that you're running this test.
You go to some subdirectory which has your code project in it.
You have a .env in there. And maybe it has some aws authentication tokens in there.
So the example I used when I was demonstrating or showing this to Stan and Phil earlier this week.
Was I said, let's say you've got two cloud providers, right? You have the same password.
Look, you have two cloud providers. You have DigitalOcean and Linode.
Right. So there were two separate directories. There's the DO directory and the Leno directory, right? You go into the do directory You run your bash script As soon as it launches because bash dv has been set to .env.
There's a hidden.env file in that DO directory. Inside there are auth tokens to access your cloud instance Up on DigitalOcean.
So your program runs. You're good to go. And you see the out of there, you CD into your Linode directory. There's another .env file in there It has all tokens in there Let's do the exact same thing as before, except not with Digital Ocean Stein.
But within the node. And again, you're good to go. Different auth tokens.
Right? Same thing. Third directory. This is local to your system.
Right. There's no .env file there at all. Vashin v doesn't execute.
Right. And nothing happens. So you just have the raw shell script there.
So that was the example that I gave. Couple things to keep in mind.
Number one, you don't want to ever want to check in your .env file into your git repository.
Matter of fact, it's a really good idea to place that file if that's what you're going to use it That's the name of the file you're going to use.
In your .gitignore that will prevent it from happening. Second thing to note, right, is that right there's usually an order to configuration when it comes to most programs.
Most programs written today And it usually kind of goes along the lines of something like this.
There's usually a config file somewhere Piping your dot files, might be in the local directory, might be some combination of that.
Then that's usually checked. It's usually loaded first.
Next thing that it looks at is usually the command line arguments.
There might be command line arguments. That will override what's in the config file.
That's typically what happens. A third thing is a third keyword parameters. We talked about those before a little bit.
If you set a keyword parameter, you have a one-time use case Right. Of overriding even overriding even the command line arguments, right? You're just using a keyword parameters right as a way to make this effective one-time case Again, here you only have one directory and instead of you just have it point at uh
Linode or Point at DigitalOcean. With some command line parameter.
But the key word parameter You might have the off token in there.
Thing to remember about that is you probably don't want to have your auth token if it's a plain text It might be a fire, which might be okay But if it's a plain text thing.
You probably don't like a password, for example. You don't probably want that to show up in your command history So again.env files help you out.
In that situation. That's what my research led me to tell you guys about on these files.
Anybody has anything more to say about them, I'd be interested to know.
I'm curious. When you started seeing reference to .env. I've only seen it the last few years And it seems to be a relatively recent convention.
Yes.
It is. I think the bashing in B thing has been around for a while but uh whether or not this use case right started evolving.
Most people recommend using another program. I've seen a few of these But the one that the one that the one that the one that you know this top result on your google search It's called Durr envy DIR, ENV.
And I've got references to that. In the in the reference page.
But that's nice because that's nice I think it uses .envrc.
And it does work from the command line as well. It's really nice because as you're CDing around right even changes your your your local command line your command line instance to have different environment variables.
Not just environment variables. I think it does uh other things too like functions or alias I'm not sure about that.
Any more comments or questions?
There was a question or a comment in chat.
Mm-hmm.
On here.
Ron BC was saying .env files are heavily used in JavaScript Especially Node.js since whenever it came out 10 years ago question mark
Could very well be. I don't know. I've not seen them there, but I'm not a JavaScraper.
I've seen them more recently with Docker. So…
I ran into something within the last month Or I want to say it was either a Python module or something named D-O-T-E-N-V.
Has a module specifically meant for reading dot en v files.
Pulling in some variables for use in the main application.
Yeah, I could sort of see that. Yeah. Because obviously, you know, best, you just have them but uh in other programming languages, you have to import them directly.
So like for i don't know much about Python, but in ruby There's a… built-in hash map.
Which is all uppercase e and v. And when your Ruby process starts up.
You can query that hash map For the, you know, say, you know, ENV left bracket home as a spring or whatever right close bracket, right? And it'll give you the value of the home environment That's an example.
So I think that's probably true in a lot of programming.
Even see as that. Not exactly the same way, but it is in there.
Any more comments or questions? Next slide. 68, keyword parameters.
Another code example page.
Right. Yep. I kind of previewed that before. Probably we should have moved this slide up a bit.
Um but uh Again, I just want to mention the command position That's where you put your command. You might have seen this used more in like something like make, for example.
By default, if you're in Linux. Gnu make, right, is probably going to have the cc now this is internal to make right the c c variable set to GCC.
And it's probably, we say, okay, you're going to make your program. I'm going to run CC on that or whatever, right?
So if you want to override that. For one-time use, you can say something like CC equals clay, for example.
If you have client installed. And then when you run make, it won't use GCC. It'll use Flang.
So… That's the first time I've ever saw that.
But, you know, again, we talked about keyword parameters before. This is just a slide that details and I probably should have moved it up in the sequence.
So I apologize for that.
I was using a variant of that a few weeks ago.
Where I was doing some custom code dealing with multiple versions of OpenSSL on a system.
I did a LD library path something equals the path to the directory, the version of OpenSL.
Mm-hmm.
I was wanting to work against pointed to. And that caused the dynamic linker to read that variable for that command.
And not change anything else about my environment.
Yes, that's a well-known use case. I've seen that many times.
That's usually for things like that. You're changing the default configuration.
Of their command to use something else. Another really good example of this And if we're just staying in the world of bash is the IFS.
Environment variable. I don't know if it's an environment variable. But it is definitely a built-in variable The IFS stands for input field separator And it's real good for like… changing it from a default of white space to something else.
Example I used before. Was that the path environment variable Is this colons for separators?
And if you said IFS equal to the colon or whatever You can get that.
You can loop through that thing Just by… because it will have changed it into a separate set of of directories.
So rather than just this big mud string all together with colons in the middle right Now you've got separate strings.
So I don't have, I can't have describe the exact code that you used for that, but you can't do it.
You could use it for other things too. Ifs is one of those I've used for.
And that's just staying within bash. You don't even need to run any external programs for that.
Questions, questions?
Moving to the next slide. 69, keyword parameters in other contexts.
Okay, hold on a sec.
Right. Yep. So you can use a keyword parameter And it was a little weird, right?
You're thinking it's creating a child process Well, not so much. If you use it in front of a function.
The value within the body of the function will be what that keyword parameter is set to.
When the function exits. The value of that variable if there happen to be an existing variable in the outer scope.
Will be restored to the outer scope. So… That's a way of writing one function that does different things Depending upon this particular parameter.
Another way to do this, obviously, is just by passing different parameters or different arguments to the function.
But again, this is just a way to keep that in mind that you can modify things.
Again, the way I did it was I wrote a function, I used IFS equals colon.
And, you know, and I wrote a little function I call my function, by the way, which W-I-T-Ch just just you know Because I wanted to.
Anyway… So that's a way to do that.
The other thing to remember is that sub shells.
And command groups, the parser will bark will barf at you if you try to put keyword parameters in front of them Don't know why that's the case, but within the sub-shell and within the command group Keyword parameters are just fine.
And they work just as expected. Okay. Well, we're getting close to the end, guys. We only got about maybe four slides left, five slides left, something like that.
And we're almost done. So.
Moving to the next slide. 70.
I don't know if it helps, but I put a list of the standard bash keywords in there.
Oh, okay. Link. Two.
In the chat.
No, just a list.
Oh, okay. Okay. Well, thank you.
Have to grab that and maybe I'll add that to the for the reference slides before we put it up there.
Problem is, is I can't get to this the chat. So I'm going to ask my buddy Phil to do that for me.
Well, I say that I can always send it to you.
If he's still awake.
Oh, yeah, I'd appreciate that.
Or send it to Stan and then he can… Do whatever.
Okay.
I think we could pull it out of the chat.
Okay.
All right. So where are we staying?
70 external scripts. Running external script is launching a fresh copy of bash to run the script.
Okay, let me see.
Okay. Yeah, this is a really pedantic point here. But all I wanted to point out here, right, and this was something It took me a long time to learn this.
Right. When I first started running first the born shell and then bash.
Is that an external script is nothing more than just another program.
So the program that you're currently running, if it's bash And if you were running in another script.
Which is also bash, right? Sorry, folks. That's its own copy.
Its own little world, right? It doesn't share anything now remember You did create a tile process with fork.
But then the next thing you did was exec. Right. And so bash came into being and overwrote that child process.
But it's not the same bass you're running. So the only way to communicate variables from a parent process to a child process that is an external script.
Is using an environment variable. Other ways of doing that obviously are piping something into the script.
Or passing it as a parameter on the command line. You know, it's up to you how you want to code that right But if you're using variables, just keep in mind. And also, external scripts are not going to be able to change the parents
Variables, environment, or otherwise Other than that other than they could do like we talked about before they could write something to standard out you could capture it using a sub-show expansion aka a command substitution.
You could use a return code if it's less than 255.
So that's all I have to say in this slide. If there's no more questions or comments.
Um. Are you going to talk about source or the dot?
Sure. Yes, yes, yes.
Yes, we're just about to get there. Almost there.
Okay.
Going on to going on to slide 71, the eval built-in command.
Okay, hold on.
Okay. Now, a couple of things to say about this. The eval command takes a string And it runs it in the current session.
There's not a sub show. There's not another child process created.
I mean, obviously. You know whatever's in that string right could create child processing.
I mean, I'm not saying that. I'm just saying that by default, nothing's happened there.
Once again, there's a situation with the uh double quoted and single quoted string.
Now, this isn't really going to apply much to the eval command, if you're just running it in the outer script.
Where this comes into play is if you're using eval, let's say in the body of a function.
Right. And you use a single quoted string versus a double quoted string, right?
If you do it as a double quoted spring then the eval function will have expanded that variable to its value when you declared the function.
That's an important thing to remember. Unless the variable that you were expanding is a local variable i know This is very complicated, but that's the case.
However, if you use a single quoted string. Then when your function executes.
It will use the value. So just just like we talked about with an ale is.
Right. When you define it. Versus when you invoke it.
So that sort of matters. But if you're just using it in the main body of your script.
That doesn't really have that big of a bearing on it.
Anything changed inside that string? Will be changed and the outer body is Or if it's a local variable and you're in the function anything changed within the function.
Okay, change. Inside there. So it reads it, it writes it.
It's just like it's living right there. The advantage of eBIL And by the way, eval is highly frowned upon. I'll get to that in one second.
But the advantage of it is that You can construct something to broaden.
By concatenating some strings together. So you might not know what you want.
Until you're ready. And then when you're ready, you can eval that variable which happens to be a string And it will expand out and become the commands to run.
So there could be some use case for that. Remember I mentioned where to stop there that BASH has a read.
Amanda has an email command. Something equivalent to a print command and you can make a loop.
So you can use read eval and you can use echo and the wild loop to make your own REPL.
Just out of bashkit. The reason why it's a little bit frowned upon is that Well, where did you get that strength?
Did you get that spring from user input? Ooh, bad, bash programmer, bad bash programmer.
You should do that, right? You can't trust… anybody's input, right? Not without thoroughly vetting it Thoroughly sanitizing it.
So don't do that. If you're keeping it all self-contained and just within the script and you know what you're doing You might be able to use eval too to some great effect.
So one interesting point about the eval command is that keyword parameters work here just like you'd expect.
So if you put a keyword parameter before the eval command It will take effect.
Inside the body, once that eval is is been run However, those variables, if they have the same name.
And they have different values outside. Of the keyword parameter assignment that you did, they will be restored to what they were prior to it. So in other words.
The keyword parameter the one-timeness of the keyword parameter on eval works just like it does For external programs and programs four functions.
Okay, any questions about eval before we get to, we have two more big slides.
And then we'll be done.
Moving to the next slide, 72. The exact built-in command.
Okay, that come here.
Okay, yeah. A couple of things to say about the exact man.
It's not going to return, guys. It's going to override your bash script.
With whatever path that you've given it there. That means that you can't use… And pipe the output of exact into a pipeline. You can't do that. You can't check the return value.
Of whatever you've exacted. Now, there's one minor little exception to that.
And that is that, and it's a very, very minor If… the exact command is given an invalid path name.
Or if it's a valid path name. But you or your script does not have permissions to run it.
Uh say maybe it doesn't have execute permissions Or maybe you just don't have permissions to run it.
You're not super user or whatever. Well, Ezek will fail.
And it will set its exit code do something non-zero.
So you can use it in that situation. You can use it in an if statement or in a logical and or or statement.
However, if it succeeds. It's not returning. It is literally overwrites. It is effectively the exact system function call.
It's going to override that. It also passes any other parameters that you pass ask the file name to the program that will run.
So one final thing to say about exec is that It also works In a little handy way, I should take that back.
There's two things to say about that. The program that I used to create This slide deck.
Uses exec quite heavily. And the reason I do that is because I wanted to mimic the behavior of commands like apt and git and docker.
Because they are the command slash subcommand pattern.
So you'll say apt update or apt install You'll say git, git add or git commit, get status.
You'll say Docker run or Docker pull. So these are all commands and subcommons.
Oh, I have a program called Make Deck. It's actually McDeck, M-K-D-E-C-K- And I've got all these things, Ed.
Make that status and make that run. And make that clobber and make that clean. And there's a whole bunch of They're all documented. They're all in the tower ball that you can download makes heavy use of exec. And that's how it does it. It just literally
It looks at dollar sign one, this exact dollar sign one.
And off it goes. It does have a little… security wrapper around that.
So you can't mess it up. But that's the situation. Final thing to say about exec.
Is that if you're in the REPL in the command line You have the ability Let's say you've got a situation where you've been doing a whole bunch of testing And you set some variables you've created a bunch of functions and a bunch of aliases and
You changed your directory and you did a whole bunch of stuff, right?
And now your whole environment is totally polluted. With all this crap that you don't want anymore.
Well, you can fix that two different ways. You probably would just say, I'll just close the terminal Open up a new terminal. Yep, that does work.
But a simpler, more fast, efficient way to do that is you just say exec bash And away you go. And it's just as if you never did anything.
It reloads your bash. Loads of BASH profile, load Jabash RC, And you're back to where you were.
So that's all we have to say about exec. And we have one more command.
And when we're done.
Going on to the next slide. 75, the source command.
Yay!
Yeah. Okay. The source command.
Is a very much a lot like a lot the eval command, right? We just got done talking about that So you could even imagine writing your own version of the source command. All you really would need to do is like do a read
Of a file into a variable And then eval that variable.
That would basically kind of be the effect of the source command.
That's pretty much all it does. It runs in the current session, doesn't create any subtle processes.
Of course, they individually could do that. It runs in place. Any variables that were set prior to the source command are available to the file in the source command.
Any variables that the source command creates or reads or writes of or modifies are now available after the source command exits.
So source command is kind of like Copy and paste it goes out grabs that file, pastes it in where the source command's at.
Starts executing it and then everything just continues on. Two other things to say about the source command.
One, it is not… And I apologize if I mispronounced this word.
The source command is not the source command Itempotent. Now, that's another one of those highfalutin words that Phil tells me I shouldn't be using.
But what it means essentially is this. It doesn't remember.
What did what? So if you have the following situation.
Your main script is your main script is script A. Script A sources B.
B versus C. These sources say whoops.
You just created an infinite loop. The only way out of it is either hit control C or terminate the process.
I don't know if that's a bug in BASH. You know, just something they never addressed I don't know, but it doesn't remember.
That A was already in play, B was already in play.
And when C got around to recursively trying to call A again.
It will do that. Keep that in mind. Don't do that.
Number two, same thing, a similar thing. The source command is not like I don't know, the import command in Python the required relative command.
In Ruby or i don't know what JavaScript has, but it has something Because it does not preserve the relativeness of that path. So again, we're going to use the abc example again A versus B.
Bees or sustain. Now let's say that B is relative to A.
Right. And then B wants to source C. And C is relative to B.
Well, when B is running. It's in the same directory as A.
Now, obviously, it can change its directory, but I'm just saying that by death but foe, it's running in A.
So when it calls source of seed. It's going to call it relative to a that may or may not be where she is.
Now, if they're all in the same directory, this will work.
But if you've got a library of files, which I do have.
I have a bunch of scripts in my home directory. And I will call them.
From scripts that I'm running for some other project. I can't have those scripts.
They can't call other scripts that are relative to them. The way I get around that is I explicitly source every file uh with its you know if I'm using relative thing Obviously, if you use full path names.
Or use something like tilde slash Right. That'll work.
It doesn't preserve it. So it's not equivalent to the import command.
Or the required relative command. So a little bit not as easy to use.
But there are ways around. That, folks, is our… an ultimate slide.
And our next slide is just the conclusion. And then we'll be done.
Any questions about the source command? Before we go.
Sure.
I want to make a comment just briefly. On your A calling B, B calling C, and then C calling A.
Okay.
Being an endless loop. Not necessarily. If you tell If you have a condition in there to end based on a certain condition.
It does not endlessly loop.
Mm-hmm. You're right about that, except that uh If you don't, it will. That's just the warning that I was trying to make there.
Of course. Of course.
In other words, if that's the only thing you've done is you wrote a a program called Aid. And the only thing it did was source b And the only thing Lord B did was source c and the only thing source B did, or C did was source A again.
He goes, I tried that. And it goes into an infinitely.
But obviously, yes, you can get away with not doing that, right?
Sure.
Yeah, again, you have to like put guards So in other words, like, hey, one thing to think about is um in the C programming language.
They have the preprocessor. And they have include files. Those are usually .h file.
So your various .c files have these like pound sign includes Right. And they include some .h file.
Well, a lot of times you can get into a situation where you set some defines and you set some parameters and things And you don't want to re… source or rather re-include something that you've already included.
That could like really mess up your day. So the typical the style that I learned, and it may not necessarily be the most up-to-date modern sea usage that's out there. I think there was something in C++ called pragma once or something i think
Anyway, but the style I learned to use where she put an if def around the entire header file.
So you say if def something like if def underscore uppercase name of header underscore H uppercase And then very bottom of the file, you say end diff Inside that if def you immediately define that as a valid variable Excuse me, as a valid preprocessor directive.
So you say define underscore header underscore h right that way If something else includes that error file and then that thing includes, you know, it gets in the situation we just talked about you don't get into this recursion thing.
Because it says, hey, oh, I can't get in there because Look, I've already defined underscore header underscore H.
So I'll skip this whole thing. Because, you know, it wasn't defined.
I'm sorry, it wasn't if deaf. It would have been if and dev.
Right. So if not defined. So you could put those kinds of guards around your source statements and that would that would work as well.
So thanks for pointing that out. That was Randy, right?
I believe it was.
That's correct.
Okay.
Squirrel.
Okay, last slide. And we're out of here, guys. Man, how long have we been running?
I think we originally tied it Is that two and a half? That's three hours?
9.30. 930 St. Louis time.
Yeah, three hours from three hours
Zip3? Okay, yeah, we said to… Two hours and 15 minutes before but we had a lot of really interesting And I want to thank you guys all for doing this stuff again.
Reiterating slides and uh slide presentation scripts and references all will be available Up on the website.
Shortly, not tonight. But probably tomorrow.
And… Just reiterating here in the conclusion, the things that we learned about today And are there any final questions?
I don't have any final questions. This is Randy again. I just want to say thank you for going through the trouble of the details that you put together in putting this together and all good information.
And if nothing else, also…
Valuable in a good programming practices as well, especially here in Bash.
Right. Yeah, exactly. If you have nothing else to take away Use iterator values.
Or iterator style variable names or loops.
Don't use them for other things. So… Yeah, keep that stuff. Anyway, uh, uh, uh, uh.
Appreciate everybody's comments. I learned a lot of stuff I didn't know.
From all you guys and greg and Randy and Grant and everybody else that had anything to say.
And… we're going to take the that list and we're going to add it to the references that Randy supplied, that list of Yeah, keep in mind, you don't want to create a variable name that has the same value or the same name
As a keyword. I didn't know if I tested that or not.
That would work, but that's probably just not good programming practice.
One little weird comment. We just determined this uh a day or two ago When we first did the code slides for this deck.
The slide deck. We used a code fence around it where we had uh grave accent, grave accent, grave accent.
And then bash and then you know the whole cold side and then Three more grave accents.
That caused a weird problem. With the markdown, the MA bar.
Thing and what it did was It's syntax colored.
Springs that were bash keywords. Or bash built-ins. For example, I think one of the slides i said This is the first time we set this variable.
And this is the second time we looked at this variable, right?
And the word time is something that they know about or whatever, so they colored it a different way.
So we took that out. Made the slides a little bit nicer.
But just something to keep in mind if you ever do use that MARP program.
Uh uh Yeah, there's a lot of little things you need to know. Use that MARP program.
But that's one of them.
Well, Ed. Thanks for all the time that you put into this. And you can send me the $5 check when you get ready.
Okay.
You're getting $5. I thought it was making sense Well, crap.
Gary.
It should be $10 now.
You're going to do it here, 20.
That's right. He did two things. That's right.
I will say that I will say that As you were talking about escaping things from the inside out.
It really reminded me of some of them things I've done in macro for where you really have to understand scoping and what happens when to be.
Even being the m4 command? Yeah, okay, sorry. Just wanted to clarify that. Yeah.
Yes. That's okay.
Go ahead.
It behooves you to understand what is happening under the hood.
I mean, you can do some black magic if you know how to do it.
But it messes with your head.
Yeah, there's a whole lot of stuff I did not cover.
Tonight, because we'd have been here till tomorrow. Oh.
Gary, I suggest we do some bookends and shut down the recording.
That sounds like an excellent idea. This has been the St. Louis Linux users group the date is Thursday.
The 17th of April, 2025 Our speaker tonight has been ed howland Ed has been talking to us about uh bash In bash variables, scopes.
Lifetimes and the difficulties they cause. And there's a comment.
Thank you all very much for Everybody in the audience was also good contributors. We had a lot of audience participation.
Good boy, Rover.
Thank you all