[MIT公开课(计算机教育中缺失的一课)] 1.Overview+Shell

时间:2020-8-28 作者:admin


(该系列文章大部分内容来源于MIT课程笔记,加入了个人的理解、原笔记中没有的细节和其他的需要理解的内容)

下一讲:Shell工具与脚本


目录


The Shell

1.Shell是什么?

如今的计算机有着多种多样的交互接口让我们可以进行指令的的输入,从炫酷的图像用户界面(GUI),语音输入甚至是AR/VR都已经无处不在。 这些交互接口可以覆盖80%的使用场景,但是它们也从根本上限制了您的操作方式——你不能点击一个不存在的按钮或者是用语音输入一个还没有被录入的指令。 为了充分利用计算机的能力,我们不得不回到最根本的方式,使用文字接口:Shell。虽然各种平台支持不同的Shell,但是其核心功能都是一样的:它允许你执行程序,输入并获取某种半结构化的输出。

这里我们使用Bourne Again SHell, 简称 “bash” (现在流行的zsh是兼容bash的,MIT老师用的是bash,博主演示的是在Mac下使用zsh)

在mac终端切换bash和zsh:

切换到bash:bashchsh -s /bin/bash
切换到zsh:chsh -s /bin/zsh
重启终端后有效。


2.使用shell

当您打开终端时,您会看到一个提示符,它看起来一般是这个样子的:

missing:~$ 

这是shell最主要的文本接口。它告诉你,你的主机名是 missing 并且您当前的工作目录(”current working directory”)或者说您当前所在的位置是~ (表示 “home”)。 $符号表示您现在的身份不是root用户(稍后会介绍)。在这个提示符中,您可以输入 命令 ,命令最终会被shell解析。最简单的命令是执行一个程序:

missing:~$ date
Fri 10 Jan 2020 11:49:31 AM EST
missing:~$ 

这里,我们执行了 date 这个程序,不出意料地,它打印出了当前的日前和时间。然后,shell等待我们输入其他命令。我们可以在执行命令的同时向程序传递 参数 :

missing:~$ echo hello
hello
missing:~$ 

上例中,我们让shell执行 echo ,同时指定参数hello。echo 程序将该参数打印出来。 shell基于空格分割命令并进行解析,然后执行第一个单词代表的程序,并将后续的单词作为程序可以访问的参数。如果您希望传递的参数中包含空格(例如一个名为 My Photos 的文件夹),您要么用使用单引号,双引号将其包裹起来,要么使用转义符号\进行处理(My\ Photos)。

但是,shell是如何知道去哪里寻找 date 或 echo 的呢?其实,类似于Python or 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

当我们执行 echo 命令时,shell了解到需要执行 echo 这个程序,随后它便会在$PATH中搜索由:所分割的一系列目录,基于名字搜索该程序。当找到该程序时便执行(假定该文件是 可执行程序,后续课程将详细讲解)。确定某个程序名代表的是哪个具体的程序,可以使用 which 程序。我们也可以绕过 $PATH ,通过直接指定需要执行的程序的路径来执行该程序。


3.在shell中导航

shell中的路径是一组被分割的目录,在 Linux 和 macOS 上使用 / 分割,而在Windows上是\。路径 /代表的是系统的根目录,所有的文件夹都包括在这个路径之下(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

通过cd -命令可以回到上次的目录环境下,cd ~默认回到根目录:

lilhoe@LilHoedeMacBook-Pro ~ % cd /Users/lilhoe/Downloads                
lilhoe@LilHoedeMacBook-Pro Downloads % pwd
/Users/lilhoe/Downloads
lilhoe@LilHoedeMacBook-Pro Downloads % cd ~
lilhoe@LilHoedeMacBook-Pro ~ % pwd 
/Users/lilhoe
lilhoe@LilHoedeMacBook-Pro ~ % cd - 
~/Downloads
lilhoe@LilHoedeMacBook-Pro Downloads % pwd
/Users/lilhoe/Downloads
lilhoe@LilHoedeMacBook-Pro Downloads % 

为了查看指定目录下包含哪些文件,我们使用ls 命令:

missing:~$ ls
missing:~$ cd ..
missing:/home$ ls
missing
missing:/home$ cd ..
missing:/$ ls
bin
boot
dev
etc
home
...

除非我们利用第一个参数指定目录,否则 ls 会打印当前目录下的文件。大多数的命令接受标记和选项(带有值的标记),它们以- 开头,并可以改变程序的行为。通常,在执行程序时使用-h 或 –help 标记可以打印帮助信息,以便了解有哪些可用的标记或选项。例如,ls –help 的输出如下(zsh):

lilhoe@LilHoedeMacBook-Pro ~ % ls -help
total 0
drwx------@  3 lilhoe  staff    96B  2 15  2020 Applications/
drwx------@ 25 lilhoe  staff   800B  8 17 18:27 Desktop/
 0: group:everyone deny delete
drwx------@ 42 lilhoe  staff   1.3K  8 26 14:23 Documents/
 0: group:everyone deny delete
drwx------@ 36 lilhoe  staff   1.1K  8 26 18:50 Downloads/
 0: group:everyone deny delete
drwx------@ 80 lilhoe  staff   2.5K  7  6 11:58 Library/
 0: group:everyone deny delete
drwx------+  6 lilhoe  staff   192B  7  3 15:57 Movies/
 0: group:everyone deny delete
drwx------+  8 lilhoe  staff   256B  5 17 15:53 Music/
 0: group:everyone deny delete
drwx------+  7 lilhoe  staff   224B  2 25  2020 Pictures/
 0: group:everyone deny delete
drwxr-xr-x+  4 lilhoe  staff   128B  1  1  1970 Public/
 0: group:everyone deny delete
drwxr-xr-x   9 lilhoe  staff   288B  7 21 19:02 wekafiles/
lilhoe@LilHoedeMacBook-Pro ~ % 

这个参数可以打印出更加详细地列出目录下文件或文件夹的信息。首先,本行第一个字符d 表示 missing 是一个目录。然后接下来的九个字符,每三个字符构成一组。 (rwx). 它们分别代表了文件所有者(missing),用户组 (users) 以及其他所有人具有的权限。其中 -表示该用户不具备相应的权限。从上面的信息来看,只有文件所有者可以修改(w) , missing 文件夹 (例如,添加或删除文件夹中的文件)。为了进入某个文件夹,用户需要具备该文件夹以及其父文件夹的“搜索”权限(以“可执行”:x)权限表示。为了列出它的包含的内容,用户必须对该文件夹具备读权限(r)。对于文件来说,权限的意义也是类似的。注意,/bin目录下的程序在最后一组,即表示所有人的用户组中,均包含x权限,也就是说任何人都可以执行这些程序。

还有的命令是需要掌握的,例如 mv (用于重命名或移动文件)、 cp (拷贝文件)、rm(删除文件)以及 mkdir (新建文件夹)。mv和cp都接收两个参数,第一个为旧路径(或文件名),第二个为新路径(或文件名)。rm和mkdir接收一个参数,在Linux下,删除默认是非递归的,所以不能用rm指令删除一个文件夹,可以传递一个“-r”来递归删除(详情查看man rm手册)。另一种删除文件夹的安全命令:rmdir,只有当文件夹为空时才允许删除。

lilhoe@LilHoedeMacBook-Pro Downloads % mv 课表.numbers kebiao.numbers
lilhoe@LilHoedeMacBook-Pro Downloads % cp kebiao.numbers /Users/lilhoe/Desktop/Kebiao.numbers
lilhoe@LilHoedeMacBook-Pro Downloads % mkdir lilhoe
lilhoe@LilHoedeMacBook-Pro Downloads % rm kebiao.numbers
lilhoe@LilHoedeMacBook-Pro Downloads % mkdir hisd
lilhoe@LilHoedeMacBook-Pro Downloads % rmdir hisd
lilhoe@LilHoedeMacBook-Pro Downloads %

如果您想要知道关于程序参数、输入输出的信息,亦或是想要了解它们的工作方式,请试试 man 这个程序。它会接受一个程序名作为参数,然后将它的文档(用户手册)展现给您。注意,使用q 可以退出该程序(mac的terminal使用q只是退出修改模式,需要键入:wq才会保存退出)。

man ls

[MIT公开课(计算机教育中缺失的一课)] 1.Overview+Shell


4.在程序间创建连接

在shell中,程序有两个主要的“流”:它们的输入流和输出流。 通常,一个程序的输入输出流都是您的终端。也就是,键盘作为输入,显示器作为输出。 但是,我们也可以重定向这些流!

最简单的重定向是 < 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

“<“表示重定向该该文件的内容作为输入,”>”表示将该程序的输出重定向为该文件的内容,“cat”打印文件内容,” cat < hello.txt > hello2.txt”可以代替cp命令来复制文件。

还可以使用 >> 来向一个文件追加内容,而”>”是覆盖输出:

missing:~$ cat < hello.txt >> hello2.txt
missing:~$ cat hello2.txt
hello
hello

使用管道( pipes ),我们能够更好的利用文件重定向。 |操作符允许我们将一个程序的输出(管道左侧)和另外一个程序的输入(管道右侧)建立联系,tail函数只打印输出的最后一行:

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

5.一个功能全面又强大的工具

对于大多数的类Unix系统,有一类用户是非常特殊的,那就是:根用户(root用户)。 在上面的输出结果中,根用户几乎不受任何限制,他可以创建、读取、更新和删除系统中的任何文件。 通常在我们并不会以根用户的身份直接登录系统,因为这样可能会因为某些错误的操作而破坏系统。 取而代之的是我们会在需要的时候使用 sudo 命令。顾名思义,它的作用是让您可以以su(super user 或 root的简写)的身份do一些事情。 当您遇到拒绝访问(permission denied)的错误时,通常是因为此时您必须是根用户才能操作。

在Shell中,在用户名后的”$“表示的是以普通用户的身份运行,想要通过root运行,则需要输入”sudo su”命令,此时变成了”#”符号,表示是通过超级用户(root)运行。

有一件事情是必须作为root用户才能做的,那就是向sysfs 文件写入内容。系统被挂载在/sys下, sysfs 文件则暴露了一些内核(kernel)参数。 因此,您不需要借助任何专用的工具,就可以轻松地在运行期间配置系统内核。(Windows or macOS没有这个文件)

例如,您笔记本电脑的屏幕亮度写在 brightness 文件中,它位于:

/sys/class/backlight

通过将数值写入该文件,我们可以改变屏幕的亮度。现在,蹦到您脑袋里的第一个想法可能是:

$ 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

我们得到了一个错误信息。我们已经使用了 sudo 命令!|、>、和 < 是通过shell执行的,而不是被各个程序单独执行。 echo 等程序并不知道|的存在,它们只知道从自己的输入输出流中进行读写。 对于上面这种情况, shell (权限为您的当前用户) 在设置 sudo echo 前尝试打开 brightness 文件并写入,但是系统拒绝了shell的操作因为此时shell不是root用户。

明白这一点后,我们可以这样操作:

$ echo 3 | sudo tee brightness

因为打开/sys 文件的是tee这个程序,并且该程序以root权限在运行,因此操作可以进行。 这样就可以在/sys中愉快地玩耍了,例如修改系统中各种LED的状态(路径可能会有所不同):

$ echo 1 | sudo tee /sys/class/leds/input6::scrolllock/brightness

另外有一些指令:

  • xdg-open(opens a file or URL in the user’s preferred application):在linux中,通常用命令行打开文本文件,比如用命令gedit、more、cat、vim、less。但当需要打开其他格式文件时,比如pdf、jpg、mp3格式文件,咱们通常做法是进入到文件所在的目录,双击打开,很影响效率。事实上,可以通过命令xdg-open打开这些格式文件,甚至是网页,可以像打开文件一样简单。
xdg-open { file | URL } 
xdg-open { --help | --manual | --version } 

$ xdg-open bonita.mp3 
$ xdg-open http://baidu.com 
  • grep命令用于搜索给定文件,选出符合模式的行。

NAME
     grep, egrep, fgrep, zgrep, zegrep, zfgrep -- file pattern searcher

SYNOPSIS
     grep [-abcdDEFGHhIiJLlmnOopqRSsUVvwxZ] [-A num] [-B num] [-C[num]]
          [-e pattern] [-f file] [--binary-files=value] [--color[=when]]
          [--colour[=when]] [--context[=num]] [--label] [--line-buffered]
          [--null] [pattern] [file ...]

举例:这里创建了一个文本文件test.txt,向里面输入两行文本,查找内容,符合匹配的会输出,$?(将在下一节讲到)返回上一条指令的返回值,0为成功,1为错误。

lilhoe@LilHoedeMacBook-Pro Downloads % touch test.txt
lilhoe@LilHoedeMacBook-Pro Downloads % echo "Hello" > test.txt
lilhoe@LilHoedeMacBook-Pro Downloads % echo "Hello,World" >> test.txt
lilhoe@LilHoedeMacBook-Pro Downloads % grep Hello test.txt
Hello
Hello,World
lilhoe@LilHoedeMacBook-Pro Downloads % echo $?
0
lilhoe@LilHoedeMacBook-Pro Downloads % grep what test.txt
lilhoe@LilHoedeMacBook-Pro Downloads % echo $?
1


课后练习

  1. 用 touch 在 Downloads 文件夹中新建一个叫 semester 的文件。
    首先查看touch的作用:
man touch

NAME
     touch -- change file access and modification times

SYNOPSIS
     touch [-A [-][[hh]mm]SS] [-acfhm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]]
           file ...

新建:

lilhoe@LilHoedeMacBook-Pro Downloads % touch semester
  1. 将以下内容一行一行地写入 semester 文件:
 #!/bin/sh
 curl --head --silent https://missing.csail.mit.edu

第一行可能有点棘手, # 在Bash中表示注释,而 ! 即使被双引号(”)包裹也具有特殊的含义。 单引号(’)则不一样,此处利用这一点解决输入问题。更多信息请参考 Bash quoting手册

echo \#'!'/bin/sh > semester
echo curl --head --silent https://missing.csail.mit.edu >> semester

[MIT公开课(计算机教育中缺失的一课)] 1.Overview+Shell

  1. 尝试执行这个文件。例如,将该脚本的路径(./semester)输入到您的shell中并回车。如果程序无法执行,请使用 ls命令来获取信息并理解其不能执行的原因。
lilhoe@LilHoedeMacBook-Pro Downloads % ./semester
zsh: permission denied: ./semester

权限不够
执行ls -l命令
显示-rw-r–r– …显然没有可执行x权限

  1. 使用 chmod 命令改变权限,使 ./semester 能够成功执行,不要使用sh semester来执行该程序。您的shell是如何知晓这个文件需要使用sh来解析呢?更多信息请参考:shebang

首先man chmod查看chmod的作用:

NAME
     chmod -- change file modes or Access Control Lists

SYNOPSIS
     chmod [-fv] [-R [-H | -L | -P]] mode file ...
     chmod [-fv] [-R [-H | -L | -P]] [-a | +a | =a] ACE file ...
     chmod [-fhv] [-R [-H | -L | -P]] [-E] file ...
     chmod [-fhv] [-R [-H | -L | -P]] [-C] file ...
     chmod [-fhv] [-R [-H | -L | -P]] [-N] file ...
chmod ugo+x semester
./semester

HTTP/2 200 
content-type: text/html; charset=utf-8
server: GitHub.com
last-modified: Thu, 20 Aug 2020 00:59:16 GMT
etag: "5f3dcae4-1e33"
access-control-allow-origin: *
expires: Wed, 26 Aug 2020 08:17:55 GMT
cache-control: max-age=600
x-proxy-cache: MISS
x-github-request-id: E3CC:587C:5DC7FC:62F3C9:5F46185A
accept-ranges: bytes
date: Thu, 27 Aug 2020 04:13:11 GMT
via: 1.1 varnish
age: 92
x-served-by: cache-sin18045-SIN
x-cache: HIT
x-cache-hits: 1
x-timer: S1598501592.916027,VS0,VE1
vary: Accept-Encoding
x-fastly-request-id: c3fcfd61f3b4140beb3ad9151db8219e196a26a0
content-length: 7731
  1. 使用 | 和 > ,将 semester 文件输出的最后更改日期信息,写入根目录下的 last-modified.txt 的文件中
touch last_modified.txt
./semester | grep -i "last-modified" > ./last_modified.txt

[MIT公开课(计算机教育中缺失的一课)] 1.Overview+Shell

  1. 写一段命令来从 /sys 中获取笔记本的电量信息,或者台式机CPU的温度。注意:macOS并没有sysfs,所以mac用户可以跳过这一题。
cat /sys/class/power_supply/battery/capacity

参考:
https://blog.csdn.net/weixin_34032827/article/details/85998637
https://blog.csdn.net/Ke_vin_Xue/article/details/107414410
https://www.jianshu.com/p/3a5fd088ab77

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。