Check out a free preview of the full Developer Productivity, v2 course
The "Dotfiles" Lesson is part of the full, Developer Productivity, v2 course featured in this preview video. Here's what you'd learn in this lesson:
ThePrimeagen explains how to create a script to copy directories and files in a Bash environment. He also shows how to use the `basename` command to extract the name of a file from its path.
Transcript from the "Dotfiles" Lesson
[00:00:00]
>> ThePrimeagen: We have everything we need. We already have our reliable environment now, so we just need one more script, right? To be able to set up our environment. So here's my environment idea, and maybe I'll throw this out there, cuz it's a little bit more kind of complicated.
[00:00:14]
Is that here, I'm gonna go in here, and I'm gonna jump into GIMP, and we're gonna kind of do, a quick little draw of it all, okay, sounds good? Now this isn't my normal mouse so if my lines aren't as straight as they normally are, I would definitely blame the mouse.
[00:00:29]
All right, not the user. So here's the basic idea is that I really need two operations, I need a copy directory and I need a copy file. In a copy directory, what it needs to do is it needs to go to a directory. And inside of that directory, I need to be able to take all the sub directories inside of it, and then copy those to a destination.
[00:00:51]
So that's kind of my first thing is that, you can imagine I have a folder called .config. And that's the same thing as your XDG config home, which is on a lot of on like Ubuntu, and I'm sure there's other systems that have something very, very similar. And I wanna copy my doc config, and I want to put it into my doc config, that's actually where all my dot files are.
[00:01:11]
So I want something that can that can crawl a directory and then put them over here, which means I'm gonna want to delete the previous version of whatever's in there. And I'm gonna wanna copy over the new version. File's obviously gonna be something similar, except for we want it to be a little bit different, which is I can point to any one file, take its file name, and then put it into some other directory somewhere else.
[00:01:32]
That's it, that's all we wanna build right here. So I can have just a very simple kind of copying scheme for my dot files, that's it. So it should be pretty simple, and then I can just have my dot files as a series of directories. Here's my dot config, here's my dot local.
[00:01:45]
Here's anything else that I might have in here, my zish RC, my bash RC, whatever other fun stuff you have in there. And for fun, I can even destroy my my nvim setup for this, and kind of just show you how it all works. So what we're gonna do here is, I'm gonna jump over here, and I'm gonna start by creating dev.env.
[00:02:00]
I call it dev.env, if you probably noticed that I also have something called dev.env currently on my path. If I cat out which dev env, you'll notice that, all right. This is my version of it that I already just have up and running on my system, which I just copied over all the things that I want.
[00:02:20]
I have the copying over my directories, and I have just copying over the individual files that I want, including tmux, Dev, prod, ready course ready. Whatever that one is that's for this, but I'm not gonna use it, don't worry about it. All right, so you get the idea.
[00:02:33]
You see kind of where we're going with this. I find it to be really easy, because when I want my computer to be up and running, I do that, and now I have all my files, and everything's updated from my exact environment that I have within my system.
[00:02:45]
So previously, I did the exact same thing, I just used Ansible. I would copy the config folder into my XdG config. It did this, except for it did it in like a minute, as opposed to, like that amount of time. So I just don't wanna live that life anymore, not living that life, I refuse to live that life.
[00:03:03]
Let's start off by getting the basics of the script ready. I'm just gonna copy things over. There's the whole, like, Don't Repeat Yourself mantra. I kind of generally repeat myself if I have no reason not to repeat myself. I don't wanna create a bash system. That kind of just sounds not fun.
[00:03:21]
So instead, I'm just gonna accept the fact that I'm gonna go right here and copy it, and then go back over here and paste it. Now, yay, look at that, we have our scripter. We don't need to filter, I'll keep the dry run in here. We could add in more arguments, I think mine has more arguments.
[00:03:39]
And so we'll just kind of keep it like this so you can pass in a bunch of arguments. And I still have this execute, I still have this. And then I can go dev, yeah. And I can do it again, look at that or we should probably call this thing dev env, yeah, look at that.
[00:03:57]
And so now if I, so we can say that, there we go, nice. So we have our dry run and our not dry run. We definitely wanna keep dry run for this one because these are gonna be a lot of destructive operations, a lot of deleting files. You kinda wanna make sure that when you delete a file, you feel ready to do this.
[00:04:19]
Cuz one time I had the little things crossed and so I just deleted the one I created and then that caused all sorts of problems and I was really sad, and then I copied nothing over. And so it's just always good to just have a dry run, just part of life.
[00:04:34]
It just makes life way better when you have a like second chance exits on a highway, I think that should be a thing. You should be able to miss the first one and have second one available right afterwards. This is the way I live my life, okay, that may have been the dumbest thing I've said in a long time.
[00:04:48]
All right, copy time. So let's create a function that will bring over every source directory to the target directory. So this will be that first of the two operations I want to be able to point it at a directory and tell it to copy every sub directory to a new directory's location.
[00:05:02]
Yeah, yeah? Yeah, awesome, you guys are just so great at this. So I'm gonna have something called copy_dir, and we have our from, which is gonna be 1, don't do that. We're gonna have r 2, this is where I mess it up, by the way. I have from 2 as 1, see, that's what I did.
[00:05:21]
I didn't update that number and then bought a bing, bought a boom, deleted the 2, and then that was actually the from, and then I was sad. All right, so we're gonna jump in here. I'm gonna use something called pushd, and what this does is it pushes a directory onto your location stack, because your location can also be a stack.
[00:05:43]
So that means where I'm currently at is allowed to change to a new location. And when I'm done, I can pop that directory off, right? So I can push my from and I can also popd, right? So now everywhere in between here I'm in that from directory, so everything sounds great, right?
[00:06:00]
So I can just point myself to a directory and now what I can do is I can go like this dirs= find. I'm gonna find everything in here, mindepth 1, maxdepth 1, and I want them as type directory, there we go. So now, I have every single directory that's within whatever directory I'm at and I can go for dir in dirs, look at that.
[00:06:22]
That's like modern language we're doing right here, we're looking really good. This feels really, really nice. By the way, you can also silence this because every time you push something in, it also tells you that it's changed to directory. So you can actually take its output and you can pipe it into dev null or redirect it.
[00:06:37]
Technically, this is redirect, it's not pipe. Carets are redirect standard out, pipe is take standard out and put it into the next program standard in. So it's a little bit different for those that don't know the difference, but tweaks the two, there we go. So now it'll just be quiet, it'll shut up.
[00:06:51]
If you wanted more, you could do this, this would redirect the file descriptor 2 to Dev Null, which file descriptor 2 in every program standard error. If I'm not mistaken, that's exactly what it is, 1 is standard out, 2 is standard error. 3 standard in, is that it or 0 standard in, I don't know all the numbers for all these things, but file descriptors, there's numbers.
[00:07:11]
All right, so there we go. So we got the dirs, we have all the directories, and so now we're gonna take this directory and we're just gonna copy it over to the destination, that's it. And so it should be pretty easy. So I'm gonna go like this, execute, R-M-R-F-C, that's the dangerous one.
[00:07:24]
You got that removing of the French people, you don't like that. So, if you do that and you mess it up, everything goes to hell. So,be careful with this one. So let's RMRF, our two directory. So, if there is this directory in this location, I'm gonna remove the current one and I'm going to copy over from the directory.
[00:07:45]
I'm gonna copy this directory over to the 2 directory. That's it, that's all we're gonna, so simple as that. That's all the script really is. Just remove the previous one, get the new one. So now we have this nice robust way to be able to copy a bunch of stuff over.
[00:08:00]
And so to kinda show you how this works, let's go like this. I'm gonna create a directory called config. Inside of config, I'm gonna create one called nvim. Inside of nvim, I'm gonna create an init.lua, that says, print, hello, Frontend Masters, there we go. So for those that are uninitialized into the nvim world.
[00:08:21]
In the xdg.config/nvim/init.lua, when nvim starts up, it looks for this specific script. When it sees this specific script, it runs this specific script. And that's how you configure nvim, question?
>> Speaker 2: Would you ever back up the folder before bvefore going in and deleting files?
>> ThePrimeagen: [LAUGH] Yeah, no, I mean, I personally am not, this is my system, I'm just not gonna take that time to do things like in such a robust way.
[00:08:52]
There's a very fine line, I don't know how to describe this other than this is the line of real software. There is the line of a hobby that's like, this is where a lot of times you'll find all the funs at. And then there's this line of real software, and your goal is to kind of bounce around in here.
[00:09:12]
The moment you start doing backups and stuff, you've gone into real software land, and real software land takes a lot more effort to really get going. You're gonna have to have to really think about how you do that. How many backups do you want? What happened if you have two directories name the same thing in two different locations, how much of the path do you include, right?
[00:09:29]
It also just becomes this huge nightmare. So me personally, I try to avoid real software. When I'm doing hobby type stuff or I'm building something, I try to keep it as one dimensional as possible, because this area is always really fun. It just really, really is, that's like a real thing.
[00:09:44]
And I think everyone inherently can identify with that to the deepest part of their core, like, I know that line. I've crossed that line, and then it becomes really unfun. And the worst part about crossing that line is the moment you do that project goes into the elephant graveyard, which is known as GitHub.
[00:10:00]
And so that's where it goes immediately, and then you never touch it again. It becomes the unfinished project for the rest of your life. So don't go into that realm. I believe we've done everything we need to do, it's also called having a job. All right, so there we go, you can see this is pretty much the exact same code that I already wrote once.
[00:10:17]
This looks pretty much the same. We're gonna have these things, and I'm gonna copy over this and just pipe it into my XdG config home, so we can do the exact same thing here. So I'll jump back here, and I'll go like this, copy, ooh, copy dir. I'm gonna take my config, which is at, remember, it's right here, it's on the same level, which has the nvim, and I am going to run this thing over to XdG.
[00:10:40]
I always type in an R, I don't know why, config home. There we go, so I'm gonna copy my config to my config home. So it's gonna copy each one of the directories, bada, bing, bada, boom, looks nice. And of course, since I am at least minimally smart, the first thing I'm gonna do are not dumb.
[00:10:57]
Let's call it dumb, not dumb, as you can see right here, okay? Dry run, what do we do? We rm-rf, Prime Gen config, same directory. I could fix this, I don't even care to fix that, like that's the same thing. I know it's inefficient to navigate once more on yourself, but whatever, right?
[00:11:13]
Delete the nvim folder, then I'm gonna take my nvim folder here and then put it over. Remember, we do a little pushd, popd, so the .nvim is actually in the config folder. So it's not far away, all right, fantastic. So let's just do this, I just did it, I just wiped out my nvim config.
[00:11:31]
My goodness, if I open up nvim, look at that Hello, front end masters. There we go, we have now copied over everything, everything is fantastic. It is actually working, and now I'll just quickly execute mine. If I do that, we're back up and running, how fantastic is that?
[00:11:46]
So, nvim's back. What you saw for a moment, it was very easy for me just to wipe out my setting and put my new one in place. Again, I'm just not a symlink guy, I don't like symlinks. I think that they're really, really, really convenient and that's a problem for me.
[00:11:59]
Because I will never update my stuff ever again, and I'll lose a bunch of work because they're so dump convenient. And that's that cuz I forget, I forget, this director is not symboling, so when I commit, it just didn't get that one, and so just won't do it.
[00:12:11]
It's not gonna do it, not not going to do it, all right. There we go, copy directory. Look at that, this is fantastic. So this is truly how I get my environment up and running, there we go. And just like that, we have that copy over all of the directories, all the configuration programs, but what about one-off scripts, right?
[00:12:30]
So that's the other thing we gotta do, okay? Expect a, [INAUDIBLE] look at that, don't look at that. Exact same thing, we're gonna do something like copy dir, except for I want copy file. We're gonna have the exact same thing. I wanna be able to copy from this file to this directory.
[00:12:46]
That's kind of how I think of it in my head. Just say it like that in my head and everything seems to work out pretty good. So instead, we don't need to do any sort of push because the from is a file, the to is a directory, just have that convention in my head.
[00:12:58]
But what I really want is I'm gonna need to have the name, which is what is the name of the file that I'm passing. So I'm gonna go like this, base name from. And for those that don't know what base name does, it's pretty nice. So let's pretend we have something called, here I'll go base names, pretend we have foo, bar, baz.
[00:13:16]
If you've used things like path, you probably noticed that it has, I think in Dino and in bun, it uses base name and go and all other languages, it uses base name. Because what that does is it takes the last item in the path and that's the base name.
[00:13:29]
You can also do something called dir name, this is from earlier, remember earlier? Foo bar just pops off that last one on top and says, okay, this is the directory in which the thing you're saying exists. That's why at the top right here, that's why that works, is cuz we get the bash source.
[00:13:44]
Where does it exist? Now, let's get the der name of where that file is, change directory to where that file is and then get the present working directory. So hopefully that now makes much more sense this weird little hack. I can't memorize things, it's much easier to memorize steps than it is to memorize exact text in order, or else I just forget it.
[00:14:03]
And so now I understand how it works, so it's easier. So there we go. So we have from, we have to, we have the base name, so now I have the name of the script that I'm copying. And so all I need to do now is it just do a little bit of execution here.
[00:14:17]
So what I'm gonna do is I'm always gonna remove, don't do recursively forced delete if it's not a folder, right? Because, in case you accidentally, somehow had it's a file over here, but a folder over there, you won't destroy yourself. Things will blow up, and you won't be able to do anything.
[00:14:32]
And you're, okay, yeah, I made a mistake, absolutely good. So what I'm gonna do here is, I'm gonna remove 2/ Name, and I'm gonna copy over from totoo/name, there we go. So now I've actually used the name of script. So if it's called bash RC in wherever I have located it within my environment, it will be called bash RC wherever I put it in.
[00:14:56]
So it's kinda like a nice little way to do that. And so let's just do a little kinda fake one, I'll go like this, copy file, and I'm gonna go special config, right? And I wanna put that in my home directory, all right? There we go, and I'll go in here and call special config, whoopsies, specialconfig.
[00:15:16]
Go in here, hi, there we go, wow, I didn't use foo. Well, I think I'm ruining my life right here. So whoopsie, that's my main one, I'll re-execute this one. And you'll notice that right away it says cannot remove. I should have dry run this, but I didn't.
[00:15:30]
I was so confident, should have dry ran it, didn't. Confidence way too high right now. There we go, cannot remove that location. Notice that it's the name of the file that I passed in, that's why base name is so cool that it actually is able to take that and you're able to get out that name.
[00:15:46]
So now I can just do this and you can see, okay, it does now exist, we now deleted it. Now we are copying everything over, fantastic. And if I go cat special config, whoopsies. I called it in prep when I was practicing last night. I did call it special RC, [LAUGH] Copy.
[00:16:06]
There you go, hi, fantastic. So now I have a way to copy over a set of directories, copy over special just one off scripts, and there we go. Now, we have ourselves the full dot file experience, at least in my opinion. I often find that it's very easy to want to abstract, cuz how many people when I copied over the log and executes first thought was, we should create a utils file.
[00:16:33]
I bet you a lot of people are like just create a utils just source a utils file. And you're probably right, we could easily source a utils file. You're correct, but I'm just letting you know that you don't always need to build things super complicated, because the moment you're building that, you might as well switch into a real language, right?
[00:16:50]
I always have some rules around bash is like, once you work with arrays or you start building an entire utils folder, it's probably just time to give up the fight and just go to some other language like Python or Node or something. Something that can do scripting for you that's a little bit, nicer to put together.
[00:17:05]
And so as simple as this is this, whatever it is, bash, bash can be complicated really, really quickly, and I think that's a good thing to remember. So if you keep it simple, bash tends to keep pretty simple. And so there we go, about 100 lines of code, and this is how I actually manage all my dot files.
[00:17:21]
This is almost one for one, all the same code, and so it's pretty dang simple. I really, really like it, and it feels very extensible, and I don't feel like I have anything that I'm running into that I'm very upset about. And so I'm much more happy than I was with Ansible, because with Ansible, I literally had 1000 lines of configuration for installing it was well over 1000 just because of how much verbosity that's required.
[00:17:41]
Ansible is like the Java of the world, where it's like, this is the Python on the world, right? You can do a lot, Python is extremely terse. You can do a lot in a very few things. A single property access can end up being hundreds of functions of calls.
[00:17:53]
You don't even know what's going to happen. So this is kind of the same approach. It's just like, keeping it simple, making it work, I'm very happy with it, at leas, there we go.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops