Opening scene: a dusty 1950s-era computer room with spinning mag tapes and hundreds of flashing lights. There's an imposing grey teletype machine and empty chair front and center. To the right is a huge, messy pile of printouts clearly torn off the machine and marked up with pencils. It's late at night with minimal room lighting.
Cut to opening animation: "The User Interface That Wouldn't Die!"
Sf/x a scream as the screen bursts into a pure white...
Okay, so maybe using the command line isn't quite this dramatic as we move slowly but inexorably into the year 2020. Twenty twenty. Hard to believe we're already that far into the 21st century. And remember, the very first command lines were from the Multics era, if not earlier (Multics begat Unix which begat Linux many years later). We're talking about a user interface that's been around since the early 1960s.
This leads to the obvious question, "What's the darn appeal for people to use such an ancient interface when there are fancy graphical window managers, mice, touchscreens and swanky million-color displays?"
I occasionally ask myself this very question when I crack open a terminal window and start typing at the command line, just to realize that the command-line interface is still popular because it's so incredibly efficient.
Want to list only files that have a "z" in their name? Want to check the last time that user "maria" logged in? Need to change all the filenames in that archive from your supplier so that everything's in lowercase? Want to bulk-resize thousands of images? Create an encrypted backup and automatically copy it to a cloud server? Log in to a customer's system 7,500 miles away for remote diagnostics?
Those are just a few of the millions of tasks you can accomplish with the command line in Linux, and it's exactly why smart users still are finding that CLI and using it. My guess is that a significant subset of Linux Journal readers type in a command at least weekly, if not much more frequently.
But, how well do you actually know the command line, the user interface that wouldn't die? Let's have a refresher of the commands folks likely use the most often since this is the command-line issue of the magazine.
Note: I've been writing about the command line for eons. Books I've written on the subject include Teach Yourself Unix in a Week (which turned into Teach Yourself Unix in 24 Hours when a week seemed like too much time), Learning Unix for MacOS X and the ever-popular Wicked Cool Shell Scripts.
Before you start typing in commands, it's really helpful to know how you
can tweak and modify your command line. Start with history
, and
you'll see a list of your previous commands. Each is prefaced by a number,
and you can repeat an earlier command quickly and easily with the shortcut
!number
, as shown:
$ history
1 PS1="$ "
2 uptime
3 history 10
4 ls -F
5 find . -name "*.c" -print
6 who
7 date
8 clear
9 history
$ !2
uptime
08:16:03 up 1:11, 1 user, load average: 0.17, 0.18, 0.18
$
Notice that when I did !2
to repeat command #2, the shell then showed the
command I'd matched (uptime
) followed by the result of that command
being invoked.
You also can repeat a command by using that same !
followed by a
letter or two, so I easily could repeat the find
command
(command #5) with this:
$ !f
find . -name "*.c" -print
...
That's the majority of my command-line manipulation, actually. On many systems, you also can use the cursor up and cursor down arrows to scroll through your command history, but that almost feels like cheating if I'm talking about a throwback to the teletype machine.
Trivia: /dev/tty is named after the teletype system, and it's still the mnemonic for your terminal session all these decades later. That teletype hasn't completely vanished!
There's quite a bit more you can do with the command line, including
pulling arguments from one command into another and so on, but those things are a bit
more obscure and probably not as helpful as just knowing that you always
can check and access your history with just a keystroke or two. In fact, when
developing a script, I often find myself in a cycle of !v
to edit
and !.
to
execute it once I've typed in the full commands a single time.
The most rudimentary use of the command line is to explore the filesystem. You surely already know that the Linux filesystem is an inverted hierarchical tree, right? Yes, and don't call me Shirley!
As a result, the key commands to learn for filesystem navigation are to
identify your current location in the filesystem (pwd
) and how to move
around (cd
):
$ pwd
/home/taylor
$ cd /home
$ pwd
/home
You can see that I started out with a present working directory of /home/taylor, then used the change directory command to move to /home, at which point my pwd is now, logically enough, /home.
There are two additional shortcuts for navigating the filesystem in Linux,
and those are ..
to back up one level in the system and
~
as a shortcut for your home directory. Your home directory, just to be clear,
is where you start when you've logged in to the system.
This should all make sense:
$ cd ~
$ pwd
/home/taylor
$ cd ..
$ pwd
/home
That's the basics of moving around. Pretty darn easy, really.
Moving around in the filesystem is of limited value if you can't
actually look at what's in your current location. That's what the
ls
command is for, and if there's one command that you should grow to love,
it's ls
. This is also the first command I'm considering here that has
arguments and command flags—lots of them actually. But to start, at its
most basic:
$ cd ~
$ ls
Desktop Downloads Music Public Videos
Documents examples.desktop Pictures Templates
You can see that there are eight entries in my home directory (remember,
~
is
a shortcut for home), but what are they? My notational convention is to have
directories start with an uppercase letter and have files all be lowercase, but that
doesn't mean I'm 100% consistent.
To find out, add the -F
flag to the ls
command, which appends a
symbol suffix to indicate file or directory type:
$ ls -F
Desktop/ Downloads/ Music/ Public/ Videos/
Documents/ examples.desktop Pictures/ Templates/
Everything with a trailing / is a directory.
This is a good moment to point out something pretty important. Although we see directories and files as being quite different, the Linux filesystem views them all as just objects or entities. You can rename a directory the same way you can rename a file, for example, and directories and files can move around in just about identical fashions too. In fact, Linux has a sloppy habit of using the filename suffix to identify the type of file too, so rename "resume.docx" to "resume.mp3", and suddenly it'll try to play your résumé instead of letting you edit it.
The -F
flag is useful, but -l
(that's a lowercase L) is much more useful
as it offers up what's known as a "long listing" format:
$ ls -l
total 44
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Desktop
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Documents
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Downloads
-rw-r--r-- 1 taylor taylor 8980 Mar 9 06:44 examples.desktop
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Music
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Pictures
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Public
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Templates
drwxr-xr-x 2 taylor taylor 4096 Mar 9 06:48 Videos
Now you can see permissions, ownership, group ownership, size (sort of), last modified dates and the file or directory name. Confusingly, the meaning of these values is different based on the type of file or object represented. All directories are shown with basically the same size (4K here), which is basically useless, but notice that the lone file, examples.desktop, has a different value. That's its actual size: 8,980 bytes.
It's the permissions string that's more interesting, however. Linux permissions model UNIX permissions and are based on three concentric circles of access: owner, group and everyone. To decode one of the permissions strings, know that the first character indicates type, then each three-character set is read/write/execute permission status for each of the three circles of access.
The directory Desktop, for example, has a d as its first
letter. Then the owner of the file (taylor, as shown in column 4) has
read+write+execute permission as denoted by rwx
. The group to which the
directory is assigned (also taylor in this example, as shown in column 5) has
read+execute permission, as denoted by r-x
. Finally, everyone else on the
system has read+execute only permission, meaning that only the owner of the
directory can edit or change it.
Similarly, the file examples.desktop has read+write permission for its owner,
taylor, and everyone else has read-only permission and cannot edit or change
it or its content. That's what -rw-r--r-- means. Focus on the
three-letter sequence * three access levels, and it shouldn't be too
intimidating. But there's no way around it, permissions for directories
are a bit more confusing. Suffice it to say, if you want someone to have access,
make it r-x
, and if you want them to be able to change, add and
edit, use rwx
.
Close it off? That's what --
is for.
Let's check out the topmost directory for some more interesting examples of permissions:
$ cd /
$ ls -l
total 970072
drwxr-xr-x 2 root root 4096 Mar 9 06:52 bin
drwxr-xr-x 19 root root 4060 Mar 9 06:56 dev
drwxr-xr-x 124 root root 12288 Mar 9 07:03 etc
drwxr-xr-x 3 root root 4096 Mar 9 06:44 home
lrwxrwxrwx 1 root root 33 Mar 9 06:54 initrd.img ->
boot/initrd.img-4.18.0-16-generic
drwxr-xr-x 21 root root 4096 Mar 9 06:46 lib
drwxr-xr-x 2 root root 4096 Oct 17 16:23 lib64
drwx------ 2 root root 16384 Mar 9 06:42 lost+found
dr-xr-xr-x 258 root root 0 Mar 9 06:56 proc
drwx------ 3 root root 4096 Oct 17 16:34 root
drwxr-xr-x 31 root root 880 Apr 8 08:05 run
drwxr-xr-x 2 root root 4096 Oct 17 16:23 sry
-rw------- 1 root root 993244160 Mar 9 06:42 swapfile
I've pruned the above just a bit to make it less overwhelming, but notice how much variation there is! The first letter of the permissions string can also be an "l" (lowercase L) to indicate a symbolic link. In this instance, it shows that the entry /initrd.img is actually a link to /boot/initd.img-4.18.0-16-generic. Symbolic links are super small too, as they need to contain only the destination filename. This one's 33 bytes in size.
Notice the permission for lost+found: rwx
for owner (root) and completely shut
off for everyone else. Oh, and see that swapfile? How big is it?
Fortunately, there's one more ls
flag worth mentioning:
-h
. That offers a
"human-friendly" size. Add that to the ability to specify a directory
or even a single file on the command line, and you can figure it out quickly:
$ ls -lh /swapfile
-rw------- 1 root root 948M Mar 9 06:42 /swapfile
Between cd
and ls
, you now can move around the
filesystem quickly.
Tip: when you're typing in a file or directory name, try pressing the Tab
key
to expand it. As long as it will expand to a unique word or name,
that's all you need to do, so typing /sw
and then pressing Tab would work fine in the above
example. Handy indeed!
Next up in this quick tour of the command line are the commands that let you move and copy files and folders, and the command that lets you create new directories. Moving and copying at some level are the same task, a stream of bytes moving into a new file container, but the big difference is what happens to the original file. With a move, the source file is deleted.
Let's jump back to my home directory and move that examples.desktop file to the Desktop directory:
$ cd ~
$ ls -l examples.desktop
-rw-r--r-- 1 taylor taylor 8980 Mar 9 06:44 examples.desktop
$ mv examples.desktop Desktop/
$ ls -F
Desktop/ Documents/ Downloads/ Music/ Pictures/ Public/
Templates/ Videos/
There's another handy shortcut demonstrated here. If you move or copy a file into a new directory, you can just specify the destination directory, and it'll retain its name. This means that, as shown, there's no longer a file called examples.desktop in my home directory.
You can rename it as you move or copy a file or folder, of course, so this would work fine:
$ mv Videos Desktop/my-video-archive
$
And this would work too:
$ cp Desktop/examples.desktop my-example
$
In fact, if you want to rename a file, it's the mv
command you'll
use, even if you're "moving" it within the same directory:
$ mv my-example demo-example.txt
$
There's no rename command in Linux, nor do you need one.
In the old days, UNIX commands were all lowercase and filenames also
were all lowercase and never had spaces within them. So it was easy to
mv test test.c
and know it'd work properly. Modern graphical interfaces work fine with
more complex names, so it's no surprise that there now are multiword
directories and filenames.
To deal with complex names, quote things, ideally with the double quotes:
$ ls
examples.desktop
$ mv examples.desktop "My Favorite Examples"
$ ls -l My\ Favorite\ Examples
-rw-r--r-- 1 taylor taylor 8980 Mar 9 06:44 'My Favorite
↪Examples'
Specific characters that cause trouble can be escaped with a prefacing
backslash, as the second ls
command shows. Better yet, that's from me
doing a Tab expansion, so the shell's smart enough to do this all by
itself. Helpful!
The shell also has a cool wildcard feature, which is great if you want to do any sort of bulk moving. For example, C program files are denoted with a ".c" suffix, so a directory full of C source files might look like this:
$ ls -F
demoprog* libalt.c library.c main1.c main2.c main.c
Makefile README utilities.c
Notice in this instance that the -F
flag has given a "*" suffix to
indicate that demoprog
is executable.
To reference all the C source files en masse, you could use *.c
as the
notation. Copy them all into a backup directory? Easy:
$ cp *.c ../backupdir
This proves generally true, so doing this:
$ cp * ../backupdir
will copy everything from this directory, including the executable.
There's lots more to the wildcard language of the shell, including
?
to match a single letter (so main?.c will match main1.c and
main2.c, but not main.c) and sets like [MR]*
to match M* and R*, but the
asterisk wildcard will definitely get you started.
Think about this: every command invoked on the command line automatically has an input, an output and an error device associated. By default, the input is the keyboard, and the output and error output are both the screen or terminal. But, you can change them.
To demonstrate, I'm going to introduce another command: wc
. The
wc
command does not point you to the closest restroom when you're in the UK
(yeah, bad joke!), it offers up the word count for a file. For example:
$ wc main.c
44 129 1172 main.c
This shows you that there are 44 lines, 129 words and 1172 characters in the
C source file main.c—useful. Add -w
, and it'll just report
words too: wc
-w main.c
.
But, this command also can use the < symbol to redirect input. Notice how it subtly changes the results:
$ wc -w < main.c
129
Because I used redirection, it didn't know the name of the input file.
You can use <
to redirect input, but you also can use
>
to redirect the output of the command and even >>
to redirect
output and append it to an existing file. Consider this:
$ date > uptime.log
$ uptime >> uptime.log
$
$ cat uptime.log
Mon Apr 8 12:17:49 MDT 2019
12:17:53 up 14 min, 1 user, load average: 0.23, 0.41, 0.45
$
Oops, the cat
program—short for concatenate—dumps
the contents of a file—any file, even one that's not actually readable.
In the above, the date
command created the output file (or overwrote it if it already existed, which
is a
danger if you mess up input and output files), then the second command had
its output appended. Finally, cat
showed what's in uptime.log.
If you can change the input and output of a command, can you have one command's output be the input to another command's input? That's what pipes are for, and it's one of the most powerful—and wicked cool—capabilities of the command line. There are hundreds of commands with thousands of flags and options, but once you realize that one command's output can be another's input, and that output can be hooked to yet another, well, then you realize there are millions of different commands you can form with a few dozen keystrokes.
At its most simple, how about this:
$ uptime | wc
1 10 61
$ uptime | wc | wc
1 3 24
See what I did in that second command? I used wc
the second time to count the
number of words, lines and characters that were the output of the first
wc
invocation. It's kind of a weird example, but it highlights that any command
that's in a pipe acts independently, so the second wc
couldn't know
that it was being invoked twice.
The combination of pipes and redirection offers an extraordinarily rich environment within which you can create just about any possible command you can imagine. That's where a lot of the fun of working with the Linux command line comes from. The other thing that makes it great fun for me is...
Yep, that should be no surprise since I have been writing the Shell Script Programming column here in Linux Journal for more years than I want to remember. Imagine that everything you can do on the command line you can easily do within a simple script. Add conditional statements, functions and variables—the sky's the limit on what you can do with shell scripts, and I've written hundreds of different scripts, ranging from a few lines to hundreds of lines of complex code.
You can see 101 of my best and most interesting in my book Wicked Cool Shell Scripts, or just read my column here in the magazine. Or even better, do both!
I'm way out of space, which is too bad, because I could keep going for many, many more pages. Indeed, I have, which is why I've written an introduction to the Linux command line three times in different books. None were shorter than 300 pages either. Still, I hope this has whetted your appetite to learn more about the fantastic Linux command line, and remember that both MacOS X and Windows have command-line interfaces too.
It really is "the User Interface that Wouldn't Die" after all.