The shell
https://missing.csail.mit.edu/2020/course-shell/
What is the shell?
Computers these days have a variety of interfaces for giving them commands; fanciful graphical user interfaces, voice interfaces, and even AR/VR are everywhere. These are great for 80% of use-cases, but they are often fundamentally restricted in what they allow you to do — you cannot press a button that isn’t there or give a voice command that hasn’t been programmed. To take full advantage of the tools your computer provides, we have to go old-school and drop down to a textual interface: The Shell.
如今的计算机有着多种多样的交互接口让我们可以进行指令的的输入,从炫酷的图像用户界面(GUI),语音输入甚至是 AR/VR 都已经无处不在。 这些交互接口可以覆盖 80% 的使用场景,但是它们也从根本上限制了您的操作方式——你不能点击一个不存在的按钮或者是用语音输入一个还没有被录入的指令。 为了充分利用计算机的能力,我们不得不回到最根本的方式,使用文字接口:Shell
Nearly all platforms you can get your hands on have a shell in one form or another, and many of them have several shells for you to choose from. While they may vary in the details, at their core they are all roughly the same: they allow you to run programs, give them input, and inspect their output in a semi-structured way.
几乎所有您能够接触到的平台都支持某种形式的 shell,有些甚至还提供了多种 shell 供您选择。虽然它们之间有些细节上的差异,但是其核心功能都是一样的:它允许你执行程序,输入并获取某种半结构化的输出。
In this lecture, we will focus on the Bourne Again SHell, or “bash” for short. This is one of the most widely used shells, and its syntax is similar to what you will see in many other shells. To open a shell prompt (where you can type commands), you first need a terminal. Your device probably shipped with one installed, or you can install one fairly easily.
本节课我们会使用 Bourne Again SHell, 简称 “bash” 。 这是被最广泛使用的一种 shell,它的语法和其他的 shell 都是类似的。打开shell 提示符(您输入指令的地方),您首先需要打开 终端 。您的设备通常都已经内置了终端,或者您也可以安装一个,非常简单。
Using the shell
When you launch your terminal, you will see a prompt that often looks a little like this:
This is the main textual interface to the shell. It tells you that you are on the machine missing
and that your “current working directory”, or where you currently are, is ~
(short for “home”). The $
tells you that you are not the root user (more on that later). At this prompt you can type a command, which will then be interpreted by the shell. The most basic command is to execute a program:\
这是 shell 最主要的文本接口。它告诉你,你的主机名是
missing
并且您当前的工作目录(”current working directory”)或者说您当前所在的位置是~
(表示 “home”)。$
符号表示您现在的身份不是 root 用户(稍后会介绍)。在这个提示符中,您可以输入 命令 ,命令最终会被 shell 解析。最简单的命令是执行一个程序:
Here, we executed the date
program, which (perhaps unsurprisingly) prints the current date and time. The shell then asks us for another command to execute. We can also execute a command with arguments:
这里,我们执行了
date
这个程序,不出意料地,它打印出了当前的日期和时间。然后,shell 等待我们输入其他命令。我们可以在执行命令的同时向程序传递 参数 :
In this case, we told the shell to execute the program echo
with the argument hello
. The echo
program simply prints out its arguments. The shell parses the command by splitting it by whitespace, and then runs the program indicated by the first word, supplying each subsequent word as an argument that the program can access. If you want to provide an argument that contains spaces or other special characters (e.g., a directory named “My Photos”), you can either quote the argument with '
or "
("My Photos"
), or escape just the relevant characters with \
(My\ Photos
).
上例中,我们让 shell 执行
echo
,同时指定参数hello
。echo
程序将该参数打印出来。 shell 基于空格分割命令并进行解析,然后执行第一个单词代表的程序,并将后续的单词作为程序可以访问的参数。如果您希望传递的参数中包含空格(例如一个名为 My Photos 的文件夹),您要么用使用单引号,双引号将其包裹起来,要么使用转义符号\
进行处理(My\ Photos
)。不然会创建两个文件夹.
But how does the shell know how to find the date
or echo
programs? Well, the shell is a programming environment, just like Python or Ruby, and so it has variables, conditionals, loops, and functions (next lecture!). When you run commands in your shell, you are really writing a small bit of code that your shell interprets. If the shell is asked to execute a command that doesn’t match one of its programming keywords, it consults an environment variable called $PATH
that lists which directories the shell should search for programs when it is given a command:
但是,shell 是如何知道去哪里寻找
date
或echo
的呢?其实,类似于 Python 或 Ruby,shell 是一个编程环境,所以它具备变量、条件、循环和函数(下一课进行讲解)。当你在 shell 中执行命令时,您实际上是在执行一段 shell 可以解释执行的简短代码。如果你要求 shell 执行某个指令,但是该指令并不是 shell 所了解的编程关键字,那么它会去咨询 环境变量$PATH
,它会列出当 shell 接到某条指令时,进行程序搜索的路径:
missing:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
missing:~$ which echo
/bin/echo
missing:~$ /bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
When we run the echo
command, the shell sees that it should execute the program echo
, and then searches through the :
-separated list of directories in $PATH
for a file by that name. When it finds it, it runs it (assuming the file is executable; more on that later). We can find out which file is executed for a given program name using the which
program. We can also bypass $PATH
entirely by giving the path to the file we want to execute.
当我们执行
echo
命令时,shell 了解到需要执行echo
这个程序,随后它便会在$PATH
中搜索由:
所分割的一系列目录,基于名字搜索该程序。当找到该程序时便执行(假定该文件是 可执行程序,后续课程将详细讲解)。确定某个程序名代表的是哪个具体的程序,可以使用which
程序。我们也可以绕过$PATH
,通过直接指定需要执行的程序的路径来执行该程序
Navigating in the shell
A path on the shell is a delimited list of directories; separated by /
on Linux and macOS and \
on Windows. On Linux and macOS, the path /
is the “root” of the file system, under which all directories and files lie, whereas on Windows there is one root for each disk partition (e.g., C:\
). We will generally assume that you are using a Linux filesystem in this class. A path that starts with /
is called an absolute path. Any other path is a relative path. Relative paths are relative to the current working directory, which we can see with the pwd
command and change with the cd
command. In a path, .
refers to the current directory, and ..
to its parent directory:
shell 中的路径是一组被分割的目录,在 Linux 和 macOS 上使用
/
分割,而在Windows上是\
。路径/
代表的是系统的根目录,所有的文件夹都包括在这个路径之下,在Windows上每个盘都有一个根目录(例如:C:\
)。 我们假设您在学习本课程时使用的是 Linux 文件系统。如果某个路径以/
开头,那么它是一个 绝对路径,其他的都是 相对路径 。相对路径是指相对于当前工作目录的路径,当前工作目录可以使用pwd
命令来获取。此外,切换目录需要使用cd
命令。在路径中,.
表示的是当前目录,而..
表示上级目录:
missing:~$ pwd
/home/missing
missing:~$ cd /home
missing:/home$ pwd
/home
missing:/home$ cd ..
missing:/$ pwd
/
missing:/$ cd ./home
missing:/home$ pwd
/home
missing:/home$ cd missing
missing:~$ pwd
/home/missing
missing:~$ ../../bin/echo hello
hello
Notice that our shell prompt kept us informed about what our current working directory was. You can configure your prompt to show you all sorts of useful information, which we will cover in a later lecture.
注意,shell 会实时显示当前的路径信息。您可以通过配置 shell 提示符来显示各种有用的信息,这一内容我们会在后面的课程中进行讨论。
In general, when we run a program, it will operate in the current directory unless we tell it otherwise. For example, it will usually search for files there, and create new files there if it needs to.
一般来说,当我们运行一个程序时,如果我们没有指定路径,则该程序会在当前目录下执行。例如,我们常常会搜索文件,并在需要时创建文件。
To see what lives in a given directory, we use the ls
command:
为了查看指定目录下包含哪些文件,我们使用
ls
命令:
missing:~$ ls
missing:~$ cd ..
missing:/home$ ls
missing
missing:/home$ cd ..
missing:/$ ls
bin
boot
dev
etc
home
...
Unless a directory is given as its first argument, ls
will print the contents of the current directory. Most commands accept flags and options (flags with values) that start with -
to modify their behavior. Usually, running a program with the -h
or --help
flag will print some help text that tells you what flags and options are available. For example, ls --help
tells us:
除非我们利用第一个参数指定目录,否则
ls
会打印当前目录下的文件。大多数的命令接受标记和选项(带有值的标记),它们以-
开头,并可以改变程序的行为。通常,在执行程序时使用-h
或--help
标记可以打印帮助信息,以便了解有哪些可用的标记或选项。例如,ls --help
的输出如下:
This gives us a bunch more information about each file or directory present. First, the d
at the beginning of the line tells us that missing
is a directory. Then follow three groups of three characters (rwx
). These indicate what permissions the owner of the file (missing
), the owning group (users
), and everyone else respectively have on the relevant item. A -
indicates that the given principal does not have the given permission. Above, only the owner is allowed to modify (w
) the missing
directory (i.e., add/remove files in it). To enter a directory, a user must have “search” (represented by “execute”: x
) permissions on that directory (and its parents). To list its contents, a user must have read (r
) permissions on that directory. For files, the permissions are as you would expect. Notice that nearly all the files in /bin
have the x
permission set for the last group, “everyone else”, so that anyone can execute those programs.
这个参数可以更加详细地列出目录下文件或文件夹的信息。首先,本行第一个字符
d
表示missing
是一个目录。然后接下来的九个字符,每三个字符构成一组。 (rwx
). 它们分别代表了文件所有者(missing
),用户组(users
) 以及其他所有人具有的权限。其中-
表示该用户不具备相应的权限。从上面的信息来看,只有文件所有者可以修改(w
),missing
文件夹 (例如,添加或删除文件夹中的文件)。为了进入某个文件夹,用户需要具备该文件夹以及其父文件夹的“搜索”权限(以“可执行”:x
)权限表示。为了列出它的包含的内容,用户必须对该文件夹具备读权限(r
)。对于文件来说,权限的意义也是类似的。注意,/bin
目录下的程序在最后一组,即表示所有人的用户组中,均包含x
权限,也就是说任何人都可以执行这些程序。
Some other handy programs to know about at this point are mv
(to rename/move a file), cp
(to copy a file), and mkdir
(to make a new directory).
在这个阶段,还有几个趁手的命令是您需要掌握的,例如
mv
(用于重命名或移动文件)、cp
(拷贝文件)以及mkdir
(新建文件夹)。
If you ever want more information about a program’s arguments, inputs, outputs, or how it works in general, give the man
program a try. It takes as an argument the name of a program, and shows you its manual page. Press q
to exit.
如果您想要知道关于程序参数、输入输出的信息,亦或是想要了解它们的工作方式,请试试
man
这个程序。它会接受一个程序名作为参数,然后将它的文档(用户手册)展现给您。注意,使用q
可以退出该程序。
In macOS zsh: the
ls --help
not work, should useman ls
instead.But Ubuntu works fine:
rmdir
: 只会删除空文件夹
Ctrl+L
: 清除终端并回到顶部
Connecting programs
In the shell, programs have two primary “streams” associated with them: their input stream and their output stream. When the program tries to read input, it reads from the input stream, and when it prints something, it prints to its output stream. Normally, a program’s input and output are both your terminal. That is, your keyboard as input and your screen as output. However, we can also rewire those streams!
在 shell 中,程序有两个主要的“流”:它们的输入流和输出流。 当程序尝试读取信息时,它们会从输入流中进行读取,当程序打印信息时,它们会将信息输出到输出流中。 通常,一个程序的输入输出流都是您的终端。也就是,您的键盘作为输入,显示器作为输出。 但是,我们也可以重定向这些流!
The simplest form of redirection is < file
and > file
. These let you rewire the input and output streams of a program to a file respectively:
最简单的重定向是
< file
和> file
。这两个命令可以将程序的输入输出流分别重定向到文件
missing:~$ echo hello > hello.txt
missing:~$ cat hello.txt
hello
missing:~$ cat < hello.txt
hello
missing:~$ cat < hello.txt > hello2.txt
missing:~$ cat hello2.txt
hello
Demonstrated in the example above, cat
is a program that concat
enates files. When given file names as arguments, it prints the contents of each of the files in sequence to its output stream. But when cat
is not given any arguments, it prints contents from its input stream to its output stream (like in the third example above).
在上面的例子中,
cat
是一个用来生成文件的程序。当给出文件名作为参数时,它会将每个文件的内容按顺序打印到输出流中。但是当没有给cat
任何参数时,它会将输入流中的内容打印到输出流中(如上面的第三个例子所示)。
You can also use >>
to append to a file. Where this kind of input/output redirection really shines is in the use of pipes. The |
operator lets you “chain” programs such that the output of one is the input of another:
您还可以使用
>>
来向一个文件追加内容。使用管道( pipes ),我们能够更好的利用文件重定向。|
操作符允许我们将一个程序的输出和另外一个程序的输入连接起来:
missing:~$ ls -l / | tail -n1
drwxr-xr-x 1 root root 4096 Jun 20 2019 var
missing:~$ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219
|
将左边程序的输出作为右边程序的输入
tail
可以打印输入的最后n行 e.g:-nl
or--line=1
We will go into a lot more detail about how to take advantage of pipes in the lecture on data wrangling.
我们会在数据清理一章中更加详细的探讨如何更好的利用管道
curl --head --silent google.com | grep -i content-length | cut --delimiter=' ' -f2
在ubuntu下可正常运行 mac 需要相应更改.Lets you extract the content length in bytes of goolge.com from command line. It's not a very useful thing to do, but you can see how by chaining these together, you can achieve a bunch of really interesting text manipulation effects. And it turns out pipes are not just for textual data, you can do this for things like images as well. You can have a program that manipulates a binary image on its input and writes a binary image to its output and you can chain them together in this way. You can even do this for video if you want. For example, a great way if you have a Chromecast at home. You can stream a video file like this by having the last program in your pipe be a Chromecast send program. So you can stream a video file into it, and it streams or HTTP to your Chromecast.
A versatile and powerful tool
On most Unix-like systems, one user is special: the “root” user. You may have seen it in the file listings above. The root user is above (almost) all access restrictions, and can create, read, update, and delete any file in the system. You will not usually log into your system as the root user though, since it’s too easy to accidentally break something. Instead, you will be using the sudo
command. As its name implies, it lets you “do” something “as su” (short for “super user”, or “root”). When you get permission denied errors, it is usually because you need to do something as root. Though make sure you first double-check that you really wanted to do it that way!
对于大多数的类 Unix 系统,有一类用户是非常特殊的,那就是:根用户(root user)。 您应该已经注意到了,在上面的输出结果中,根用户几乎不受任何限制,他可以创建、读取、更新和删除系统中的任何文件。 通常在我们并不会以根用户的身份直接登录系统,因为这样可能会因为某些错误的操作而破坏系统。 取而代之的是我们会在需要的时候使用
sudo
命令。顾名思义,它的作用是让您可以以 su(super user 或 root 的简写)的身份执行一些操作。 当您遇到拒绝访问(permission denied)的错误时,通常是因为此时您必须是根用户才能操作。然而,请再次确认您是真的要执行此操作。
One thing you need to be root in order to do is writing to the sysfs
file system mounted under /sys
. sysfs
exposes a number of kernel parameters as files, so that you can easily reconfigure the kernel on the fly without specialized tools. Note that sysfs does not exist on Windows or macOS.
有一件事情是您必须作为根用户才能做的,那就是向
sysfs
文件写入内容。系统被挂载在/sys
下,sysfs
文件则暴露了一些内核(kernel)参数。 因此,您不需要借助任何专用的工具,就可以轻松地在运行期间配置系统内核。注意 Windows 和 macOS 没有这个文件
For example, the brightness of your laptop’s screen is exposed through a file called brightness
under
例如,您笔记本电脑的屏幕亮度写在
brightness
文件中,它位于
By writing a value into that file, we can change the screen brightness. Your first instinct might be to do something like:
通过将数值写入该文件,我们可以改变屏幕的亮度。现在,蹦到您脑袋里的第一个想法可能是:
$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
/sys/class/backlight/thinkpad_screen/brightness
$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied
This error may come as a surprise. After all, we ran the command with sudo
! This is an important thing to know about the shell. Operations like |
, >
, and <
are done by the shell, not by the individual program. echo
and friends do not “know” about |
. They just read from their input and write to their output, whatever it may be. In the case above, the shell (which is authenticated just as your user) tries to open the brightness file for writing, before setting that as sudo echo
’s output, but is prevented from doing so since the shell does not run as root. Using this knowledge, we can work around this:
出乎意料的是,我们还是得到了一个错误信息。毕竟,我们已经使用了
sudo
命令!关于 shell,有件事我们必须要知道。|
、>
、和<
是通过 shell 执行的,而不是被各个程序单独执行。echo
等程序并不知道|
的存在,它们只知道从自己的输入输出流中进行读写。 对于上面这种情况, shell (权限为您的当前用户) 在设置sudo echo
前尝试打开 brightness 文件并写入,但是系统拒绝了 shell 的操作因为此时 shell 不是根用户。 明白这一点后,我们可以这样操作:
Since the tee
program is the one to open the /sys
file for writing, and it is running as root
, the permissions all work out. You can control all sorts of fun and useful things through /sys
, such as the state of various system LEDs (your path might be different):
因为打开
/sys
文件的是tee
这个程序,并且该程序以root
权限在运行,因此操作可以进行。 这样您就可以在/sys
中愉快地玩耍了,例如修改系统中各种LED的状态(路径可能会有所不同):
虚拟机下不可行, 物理机的linux可以.
鉴于我把Macbook air上的caps lock 键与Control键进行了交换, 即使虚拟机下可以进行相应的操作, 不会显示变化.
物理机linux可以
Exercises
All classes in this course are accompanied by a series of exercises. Some give you a specific task to do, while others are open-ended, like “try using X and Y programs”. We highly encourage you to try them out.
We have not written solutions for the exercises. If you are stuck on anything in particular, feel free to send us an email describing what you’ve tried so far, and we will try to help you out.
-
For this course, you need to be using a Unix shell like Bash or ZSH. If you are on Linux or macOS, you don’t have to do anything special. If you are on Windows, you need to make sure you are not running cmd.exe or PowerShell; you can use Windows Subsystem for Linux or a Linux virtual machine to use Unix-style command-line tools. To make sure you’re running an appropriate shell, you can try the command
echo $SHELL
. If it says something like/bin/bash
or/usr/bin/zsh
, that means you’re running the right program. -
Create a new directory called
missing
under/tmp
.
Solution:
- Look up the
touch
program. Theman
program is your friend.
Solution:
man touch
- Use
touch
to create a new file calledsemester
inmissing
.
Solution:
- Write the following into that file, one line at a time:
The first line might be tricky to get working. It’s helpful to know that #
starts a comment in Bash, and !
has a special meaning even within double-quoted ("
) strings. Bash treats single-quoted strings ('
) differently: they will do the trick in this case. See the Bash quoting manual page for more information.
- Try to execute the file, i.e. type the path to the script (
./semester
) into your shell and press enter. Understand why it doesn’t work by consulting the output ofls
(hint: look at the permission bits of the file).
- Run the command by explicitly starting the
sh
interpreter, and giving it the filesemester
as the first argument, i.e.sh semester
. Why does this work, while./semester
didn’t?
When you run a script using sh semester
, you are explicitly invoking the sh
shell interpreter and passing the script semester
as an argument. This means sh
will interpret the contents of the semester
file regardless of its permissions.
However, when you try to execute the script directly using ./semester
, the script itself must have execute permissions for the current user. If it doesn't, the operating system will not allow it to run. The execute permission can be added using the chmod
command:
- Look up the
chmod
program (e.g. useman chmod
).
- Use
chmod
to make it possible to run the command./semester
rather than having to typesh semester
. How does your shell know that the file is supposed to be interpreted usingsh
? See this page on the shebang line for more information.
- Use
|
and>
to write the “last modified” date output bysemester
into a file calledlast-modified.txt
in your home directory.
-
Write a command that reads out your laptop battery’s power level or your desktop machine’s CPU temperature from
/sys
. Note: if you’re a macOS user, your OS doesn’t have sysfs, so you can skip this exercise.虚拟机中不可行, 物理机可