Last time, we explored the command line using the Terminal app. We are going to do the same again today, expanding on what we previously covered.
Before that, though, let’s do a quick summary of the key ideas presented in the previous instalment.
- The command line is a method of controlling your computer using written instructions. This differs from the graphical user interface (GUI) that most computer-users are familiar with.
- The prompt provides useful information about who the current user is and what the current working directory (folder) is.
- Commands such as cd followed by a path allow us to change directories and move around; touch followed by a name is used to create a new file; and rm followed by a name removes a specific file.
- Tab-complete makes typing much easier. Not only do you type less overall but also, by autocompleting your instructions, your error-rate is reduced. However, when there are multiple possible auto-completions the terminal will display valid files and directories that start with the string of characters you tried to tab-complete.
That was last time.
Today, I am going to introduce new concepts at a slightly faster rate. The purpose of today is not necessarily for you to deeply internalise all the information, but more so to become aware of the concepts. To know they exist. Last time I tried to move slowly so that you were comfortable. Today is more about broadening your horizons rather than mastery.
In the future, you’ll still likely need to Google how-to-do-that-one-particular-thing that you might have read about in this article. You won’t remember it from memory, but that’s alright. The important thing is that you’re aware there is a particular thing to begin with. It’s a common trope in computer science and software engineering that everyone needs to look things up – there is simply too much information for a single individual to know it all.
The key is knowing what to look for and being able to phrase your query appropriately. That’s where the awareness aspect comes in. Mastery isn’t required if you know how to make use of all the tools at your disposal.
Let’s say I’m thinking writing a new series for ThereforeThink, following the completion of the current one on computer science. I’ve decided I want to do a series on discrete mathematics, which I believe will be additive to previous content and interesting for both myself and readers.
This is genuinely something I’ve been thinking about for a while. For the sake of today’s discussion, though, let’s say I can’t remember if I have started doing any preparation yet.
One option is for me to have a look at all the content in my ThereforeThink directory. But, how can we do this from the command line? Last time we only saw content listed when it provided confusion for tab-complete. However, there is a way to do this. We can see everything in a location simply using the ls command – for list.
Except before we do that, let me also layer on another little tactic you can implement. You can string multiple instructions together, to be executed sequentially, by separating the commands with a semicolon ( ; ).
In the below image, you will see that I execute my cd command – placing me in the appropriate folder – then ls command, all in the same line.
This then lists all the content in my ThereforeThink directory – even though I was in a different location prior to that sequence of instructions being executed. Important to understand, however, is that this is only the content at that level of abstraction.
What I mean by this is that five of the six items listed above are directories. All these directories may, or may not, contain more files and/or sub-directories of their own. Again, the ls command only lists content at that level of abstraction.
However, this not the major problem that confronts us. The primary issue is that we cannot even tell what file types each of the items are. Previously, when we saw items listed in the terminal – again, when tab-complete was confused about which file we were after – it provided us with a trailing forward-slash to denote a directory. We don’t get that in this situation.
There is a way, though. Multiple, in fact.
First, we can achieve this by passing what is known as an option or flag to the command. This is a way of customising the command. These instructions typically take the form of command, space, hyphen, letter. Not always, but often.
For our example, to see what we want we must enter the command and option: ls -F.
Sweet. That worked pretty well.
We can deduct from this output which of the names identify directories, as opposed to files. We know the trailing forward-slash symbolises a directory, but only because I told you that. I think we can do better than this. Let’s make the terminal be a little more explicit.
Before I do that, though, I’m going to jump back to my Home directory. Remember: you can do this by simply entering cd with no path.
I’m doing this so that I can do as I did before – and string multiple instructions together – but add in a third this time. This is not the practical approach; I’m simply doing this to show you that it’s possible.
Of the three instructions, two will be familiar. I’ll explain the third momentarily.
I’ve put the green rectangle on the image just to better highlight the instructions and output of interest. (But do note the cd command that is partially obstructed and could easily be missed between the two larger outputs.)
The first command within our green frame is standard at this point; moving from the Home directory to the ThereforeThink directory.
The second command we have also seen already; though, not as much. This one creates a new file called example_script with a .py extension – which denotes that it is a Python file (Python is a programming language, and one we will be using in the coming weeks).
The third command is new – as promised. Interestingly, however, it is new in multiple ways; neither of which are hard to understand.
The first part of the command is file, which basically asks the terminal, “What type is the following file?”. Simple enough. If we passed in the name of a particular file, it would tell us.
The asterisk ( * ), however, is one the wildcard operators. A wildcard operator can stand-in for other symbols and can be used in many ways. It is being used in place of a file name at this point. The asterisk is the least specific of all the wildcard operators and essentially means, “everything.”
When we combine these components into the instruction file * we are essentially asking: What types are all the files in this location?
As the above image showed, there are five directories; a file containing ASCII text (ASCII is just how text and other symbols, such as punctuation, are represented in a computer system); and an empty file (because I only just created it).
At this point, we know all the files, and their types, in the current location. However, the original intent of this exploration was to see if I had begun preparations for a series on discrete mathematics.
Let’s conclude that the search has been fruitless. Sure, I could list all the items in one of the directories by using the command ls [directory_name], or even list the content of sub-directory of one of the directories by typing ls [path] – where the specific name of the directory, or path to the desired directory, need to be substituted into the command – but let’s just assume for now that nothing of use will be found.
If I was serious about the project, I wouldn’t have hidden it so deeply. So, with that, let’s make a directory to start storing related resources within.
How to make a directory
When a mummy-directory and a daddy-directory… No, not really. Even though “parent directory” is a valid term.
The way to create a directory, funnily enough, is with the make directory instruction. However, the actual command is shortened to mkdir, which is then followed by the name you’re giving to the directory. I’m going to call this one “DiscreteMathSeries”. I can then instantiate this new directory by executing mkdir DiscreteMathSeries.
Great. Now we’ve got a directory to place things of relevance, let’s put something in there. Seeing as the example_script.py is just an empty file, let’s move that in there. It’s not exactly relevant, but we’ll alter that in just a tick.
Wait a second. How do we move a file without dragging and dropping?
We use the move command, of course. Which, in similar style to the rest (except touch) is a shortened version of the word that describes it. The way to move something is using mv [what_to_move] [path_to_end_location].
In our case, this would be: mv example_script.py DiscreteMathSeries.
No major exciting output, but that’s ok. If the terminal is displaying nothing, odds are you’re command worked. It will make itself known if it doesn’t understand what you said.
Ok, now that we have the empty file in there, we should probably name it something relevant. Otherwise we might be confused when we come back at a later point and find and empty Python file in the DiscreteMathSeries directory.
We can fix that with the mv command once more…
Renaming is moving?
Yes. Yes, it is.
This might seem strange at first – to use the mv command to rename a file – but bear with me.
Think about this as moving from one point in namespace to another.
If that’s too abstract for you, try this: you’re just moving along the spectrum of all possible names. Let’s say that there is a 5 letter limit for names, so the spectrum ranges from A to zzzzz
This is presuming capital letters come before lowercase and that numbers or other symbols aren’t included. This isn’t necessarily true when we are naming things on our computer, but it is sufficent for our example.
Now, let’s say that all the possible strings of letters – from A to zzzzz – are lined up next to each other. It might look something like this:
A, B, C, … Z, AA, AB, AC, … AZ, BA, BB, BC, … BZ, CA, CB, CC, … CZ … zzzzx, zzzzy, zzzzz.
(In math, the ellipsis symbol ‘…’ basically just means “the pattern continues”).
Although it seems hard to wrap your head around, all valid 5 letter words are contained within that spetrum from A to zzzzz. Words such as “Rolex”, “Anne”, “dodge”, “Peter”, “catch”, “GOD”, “God” and “god” are all in there.
Again, this may seem counterintuitive, but it’s like listing all the numbers from 0 to 99,999 and then thinking you can come up with a 5-number comination that isn’t on the list. Not going to happen. It’ll be there.
What we are looking at here is a combinatorial sytem, like with numbers – only on a much larger scale. When we count, using the decimal system, there’s ten possible values that any location within a string – where a string is zero or more characters — can be (0 through to 9). In the namespace equivalent, there’s 52 possible values for a single location (26 uppercase plus 26 lowercase).
This doesn’t just result in a few morepossible permutations – which is the technically correct term, as opposed to combinations. We go from 10 ^5 (ten to-the five) which is 1,000,000. All the way to 52^5 (fifty-two to-the five) which is 380,204,032.
Ok, I’m getting of point now.
The point is that a lot of different words can be made using up to 5 upper- or lowercase letters. And, with so many options availabel to us, we just shift from one location to another when we want to rename things.
We use mv to change our location in namespace.
This is basically what ASCII (which stand for American Standard Code for Information Interchange) helps us achieve. Each letter, number, and piece of punctuation is mapped to a code which represents it “under the hood”. For instance, in ASCII, the exclamation mark ( ! ) is 010 0001 in binary or 33 in decimal. An uppercase A is 100 0001 in binary or 65 in decimal.
You get the jist.
Now we have established that. Let’s first clear the screen for readability, using the clear command. Then change into the DiscreteMathSeries directory and change the name of the example_script.py file using mv.
Then as evidence of this taking effect, we will use ls.
Great. Now let’s add some content to that file.
Reading, writing and clearing text files
In computer science, when you “write to” a file — or some other place where data is stored — you are adding information. Conversely, when you want to retrieve some information from somewhere, this is termed “reading from.” You may be reading a list of names, an account balance, whether a particular seat has been reserved, or any other number of things!
For now, though, let’s just read and write some basic text. We will do this using our newly named DM_file text file.
But, before we can do that, we need to become familiar with an innocous little command, echo.
Echo is one of those it-does-what-it-says-on-the-box kind of commands. As you could have proabbly guessed, echo repeats back to you whatver you “pass” to it. For example, when I enter the command echo “Harry Potter is an overrated franchise”, it will display: Harry Potter is an overrrated franchise.
(While the echo command can work with or without quoation marks, it is considered good practice to always “wrap” your text in them. This provides clear indications to the computer about where you message starts and stops, minimising bugs and errors.)
However, we can implement this seemingly useless command to help us input text into a file without opening it up. To do this, use the redirection operators which are ‘>’ and ‘>>’. In totality, the command will look something like this: echo “[message]” >> [target_file]. This
The difference between the two redirection operators is that the single greater-than sign just writes the conent into the file from the starting position (row 0, column 0). This isn’t an issue if the file is empty; but if it isn’t, then you’ll be writing-over what is already in there. The double greater-than sign starts the writing position at the end of what is already in the file (if anything). Therefore, it “appends” to the end of the file.
Notice that the second use of the echo command didn’t repeat the information back to us. This seems like a good sign that it ended up in the desired file. But, how can we be sure?
We could read from the file and display its contents to the screen. We do this using the cat command – short for “concatenate.” Concatenation is when you string or link multiple things together, and the cat command can do this with multiple files, but we don’t necessarily need it for this. By using it on a single file, it will show us what is in that file.
The following highlights that we were, in fact, successful in writing our message to the DM_notes text file.
However, just incase there are any Harry-lovers out there, maybe we should clear away the evidence. We can do this using the command ‘: > DM_notes.txt’. The funny looking sideways smiley face (composed of a colon and then a greater-than sign with a space between) basically just means “wipe the following file.”
Again, we can see if this was successful by printing the content of the that file to the screen.
Deleting a directory
Our final skill for this introduction to the command line is deleting a directory.
There are multiple ways to go about this. For the first way, let’s create an additional directory for the purposes of deleting.
First, I’ll clear the above terminal so that we are working in a nice clean environment again. Next, we will make the new directory using the mkdir command that you are already familiar with to make our new directory. I’ll then use the file * command to show that this worked.
One way of removing this new directory is by simply executing the rmdir [name] command. This is almost identical to the mkdir [name] command, only working in the inverse direction. One names the folder to be created, the other names the folder to be deleted.
If we execute rmdir Dudd and then one more use file *, we see that this worked. Our new directory is no more. It left this world as quickly as it came into it.
That’s one way to get the job done. The other requires using a flag.
Let’s work through this example by deleting the DiscreteMathSeries directory that I am currently working in. First, I will move up into the parent directory by using the cd .. instruction.
Now that I’m outside of the DiscreteMathSeries directory (and situated back in the ThereforeThink directory), what if we just try to remove the directory like it were a file? We have already seen how to remove a file, using the rm command, so let’s try that…
Nope. No good.
If we do this, we get rm: DiscreteMathSeries: is a directory. An error message.
Why is this an issue you ask? Why does it matter if we try to delete a directory using rm?
I shall tell you. The issue stems from a directory being something that houses other things. The rm command basically says, “Hey, computer, delete this one thing that I’m about to name.”
The computer then responds with, “That’s a directory, that’s not one thing. It is potentially lots of things!”
(Clearly, I’m doing a fair bit of paraphrasing here.)
In order to delete a directory using the rm command, we need to pass the message of: delete this thing, and anything else you find that’s in there. It’s kind of analogous to the classic action movie line of “Shoot/kill/dispose of anyone that gets in your way.”
The specific flag we need to achieve this, is -r. The r stands for recursion.
Recursion is a popular term in computer science, among other places, and essentially means: achieving something by repeatedly applying a procedure to smaller and smaller components of itself. Recursion is related to decomposition, the breaking down of problems into smaller sub-components, and self-reference.
If we are to execute this command rm -r, then we are instructing the computer to delete whatever we tell it to. If, however, the computer finds something within that thing, we are giving it permission to delete it as well. Therefore, the rm -r command will delete all files and sub-directories contained within whatever we pass to it.
If we then once more use file * then we can see that this successfully removed the DiscreteMathSeries directory.
A word of caution
As you may have noticed, the rm and rmdir commands don’t give you a polite, “Are you sure you want to delete this thing?” message. Additionally, when you delete something using the command line, it stays deleted. There is no fishing it out storage limbo and restoring it.
This is the second edge of the terminal sword. I said from the outset that the command line was a powerful tool; yet powerful tools don’t exist in isolation from their dangerous uses. This is why, in the immortal words of Uncle Ben, “With great power comes great responsibility.”
Therefore, if you do wish to try and act more responsibly, using the -i flag is worthwhile. This stands for interactive. If you execute the command rm -ir [directory_name] then you will be asked if you wish to examine the contents of the directory. To which you can type yes or no – or simply y or n.
Following this, each item within the directory will be presented to you, and you will be asked if you wish to remove it. You will then use the same yes/no responses.
This is worthwhile method to consider. We all have brain-fades at times. Sometimes implementing some safety precautions when using powerful tools isn’t a bad idea.
We have covered a fait bit in this series. Yet, still, we barely scratched the surface.
Regardless, we have achieved our objective and this introduction should be sufficient to give you some awareness of how to use the command line.
In the coming weeks we will become increasingly familiar with it as we move deeper into this programming journey.
Thanks for reading.