The missing semester of your CS education--课程概览与shell

课程结构

01.课程概览与 shell
02.Shell 工具和脚本
03.编辑器 (Vim)
04.数据整理
05.命令行环境
06.版本控制(Git)
07.调试及性能分析
08.元编程
09.安全和密码学
10.大杂烩
11.提问&回答

本文档修改自这里 ,补充了一些视频中展示但配套文档中未提供的代码,以及一些注释。

shell命令&参数&环境变量

几乎所有您能够接触到的平台都支持某种形式的 shell,其核心功能是:它允许你执行程序,输入并获取某种半结构化的输出。

本节课我们会使用 Bourne Again SHell, 简称 “bash”。

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

missing:~$ 

这是 shell 最主要的文本接口,含义是主机名为 missing 且当前的工作目录(”current working directory”)是 ~ (home,主目录)。
$ 符号表示您现在的身份不是 root 用户(root用户的符号为#)。在这个提示符中,您可以输入 命令。执行 date 命令,打印出了当前的日期和时间。

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

上例中,我们让 shell 执行 echo ,同时指定参数 helloecho 程序将该参数打印出来。 shell 基于空格分割命令并进行解析,然后执行第一个单词代表的程序,并将后续的单词作为程序可以访问的参数。

~$ echo "Hello world"   
Hello world
# echo 将字符串置于标准输出,类似于c的printf函数
~$ echo Hello\ world
Hello world
# "\ "表示中间的空格"Hello world"是一个参数,而非两个参数Hello和world
# 当一行命令过长时,也可使用\进行分隔
~$ echo Hello world 
Hello world
# 注意:对于其他命令,可能会将Hello和world当作两个参数,从而得到预期外的效果

但是,shell 是如何知道去哪里寻找 dateecho 的呢?其实,类似于 Python 或 Ruby,shell 是一个编程环境,所以它具备变量、条件、循环和函数。当要求 shell 执行某个指令,它会去咨询 环境变量 $PATH

~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
# 显示的结果使用 : 分隔多个不同路径
~$ echo ls $PATH    
ls /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
# 查询结果的显示顺序为shell查找ls程序的搜索顺序
missing:~$ which echo   
/bin/echo
# 显示当前目录下执行的echo命令的所在目录

~$ which -a echo    
/usr/bin/echo
/bin/echo
# 参数 -a 打印echo的所有路径

~$ /bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
# 指定路径/bin/echo下的echo程序打印出环境变量

在shell中导航

shell 中的路径是一组被分割的目录,在 Linux 和 macOS 上使用 / 分割,而在Windows上是 \。路径 / 代表的是系统的根目录,所有的文件夹都包括在这个路径之下,在Windows上每个盘都有一个根目录(例如: C:\)。
如果某个路径以 / 开头,那么它是一个 绝对路径,其他的都是 相对路径 。相对路径是指相对于当前工作目录的路径,当前工作目录可以使用 pwd 命令来获取。此外,切换目录需要使用 cd 命令。在路径中,. 表示的是当前目录,而 .. 表示上级目录:

missing:~$ pwd  
/home/missing
# 显示当前工作目录,这里展示的是"~"代表的主目录

missing:~$ cd /home 
# 切换工作目录至"/home",见下行"missing:"后、"$"前

missing:/home$ pwd
/home
missing:/home$ cd ..    
# 切换到上一级目录,即根目录"/",在linux文件管理器点击“文件系统”,即可进入根目录

missing:/$ pwd
/
missing:/$ cd ./home
missing:/home$ pwd
/home
missing:/home$ cd missing
missing:~$ pwd
/home/missing
missing:~$ ../../bin/echo hello
hello

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

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

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

# -l : use a long listing format
missing:~$ ls -l /home
drwxr-xr-x 1 missing  users  4096 Jun 15  2019 missing
  • 字符串drwxr-xr-x的第一个字符表示文件类型,d说明这是一个目录directory
  • 后面的9个字符,每三个为一组,r 表示读取权限;w 表示写入权限;x 表示执行权限;- 表示没有对应权限
  • 前三个字符表示所有者(missing)的权限,中间三个字符表示所属组(users)的权限,后三个字符表示其他用户的权限

在这个阶段,还有几个趁手的命令是您需要掌握的。

  1. mv(重命名或移动文件)
 - Rename a file or directory when the target is not an existing directory:
   mv {{source}} {{target}}

 - Move a file or directory into an existing directory:
   mv {{source}} {{existing_directory}}

 - Move multiple files into an existing directory, keeping the filenames unchanged:
   mv {{source1}} {{source2}} {{source3}} {{existing_directory}}

 - Do not prompt for confirmation before overwriting existing files:
   mv -f {{source}} {{target}}

 - Prompt for confirmation before overwriting existing files, regardless of file permissions:
   mv -i {{source}} {{target}}

 - Do not overwrite existing files at the target:
   mv -n {{source}} {{target}}

 - Move files in verbose mode, showing files after they are moved:
   mv -v {{source}} {{target}}
  1. cp(拷贝文件)
 - Copy a file to another location:
   cp {{path/to/source_file.ext}} {{path/to/target_file.ext}}

 - Copy a file into another directory, keeping the filename:
   cp {{path/to/source_file.ext}} {{path/to/target_parent_directory}}

 - Recursively copy contents of a directory to another location (if the destination exists, the directory is copied inside it):
   cp -R {{path/to/source_directory}} {{path/to/target_directory}}

 - Copy a directory recursively, in verbose mode (shows files as they are copied):
   cp -vR {{path/to/source_directory}} {{path/to/target_directory}}

 - Copy multiple files at once to a directory:
   cp -t {{path/to/destination_directory}} {{path/to/file1 path/to/file2 ...}}

 - Copy text files to another location, in interactive mode (prompts user before overwriting):
   cp -i {{*.txt}} {{path/to/target_directory}}

 - Follow symbolic links before copying:
   cp -L {{link}} {{path/to/target_directory}}

 - Use the first argument as the destination directory (useful for xargs ... | cp -t <DEST_DIR>):
   cp -t {{path/to/target_directory}} {{path/to/file_or_directory1 path/to/file_or_directory2 ...}}
  1. mkdir(新建文件夹)
 - Create specific directories:
   mkdir {{path/to/directory1 path/to/directory2 ...}}

 - Create specific directories and their [p]arents if needed:
   mkdir -p {{path/to/directory1 path/to/directory2 ...}}

 - Create directories with specific permissions:
   mkdir -m {{rwxrw-r--}} {{path/to/directory1 path/to/directory2 ...}}

文件重定向与管道

在 shell 中,程序有两个主要的“流”:输入流和输出流。 当程序尝试读取信息时,从输入流中进行读取;当程序打印信息时,它们会将信息输出到输出流中。 通常,一个程序的输入输出流都是您的终端。也就是,您的键盘作为输入,显示器作为输出。 但是,我们也可以重定向这些流!

  1. 最简单的重定向是 < file> file。这两个命令可以将程序的输入输出流分别重定向到文件:
missing:~$ echo hello > hello.txt  
# 将字符串“hello”输出(即写入到)hello.txt(没有该文件会自动创建)
missing:~$ cat hello.txt    # 读取hello.txt的内容
hello
missing:~$ cat < hello.txt  # 从hello.txt文件读取
hello
missing:~$ cat < hello.txt > hello2.txt # 从hello.txt读取内容,将其输出到hello2.txt,hello2.txt如果原本有内容,会被覆写
missing:~$ cat hello2.txt
hello
~$ echo hello >> hello.txt  # 使用 >> 会将内容添加到文件末尾
~$ cat hello.txt
hello
hello
  1. 使用管道(pipes),我们能够更好的利用文件重定向。 | 操作符将左侧程序的输出,作为右侧程序的输入。
missing:~$ ls -l / | tail -n1
drwxr-xr-x 1 root  root  4096 Jun 20  2019 var
# 先在根目录“/”执行"ls -l"
# 对得到的结果,执行tail -n1,即输出最后一行
missing:~$ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219
# curl获取网址google.com的head内容
# grep在curl返回的内容中查找含有"content-length"的行(查找时不区分大小写)
# 对找到的行,以' '为分界符,显示第二项,若'-f2'改为'-f1',会输出'Content-Length:'

如果想更清晰地了解上一行的命令,可以逐个管道执行,即:

~ $ curl --head --silent google.com
HTTP/1.1 301 Moved Permanently
Content-Length: 219
Cache-Control: public, max-age=2592000
Connection: keep-alive
Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-ZXOmXF_8KLGeCJbHqzaZ2Q' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
Content-Type: text/html; charset=UTF-8
Date: Wed, 26 Apr 2023 14:58:10 GMT
Expires: Fri, 26 May 2023 14:58:10 GMT
Keep-Alive: timeout=4
Location: http://www.google.com/
Proxy-Connection: keep-alive
Server: gws
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 0

~ $ curl --head --silent google.com | grep --ignore-case content-length
Content-Length: 219

~ $ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219

root权限

对于大多数的类 Unix 系统,有一类用户是非常特殊的,那就是:根用户(root user)。 根用户可以创建、读取、更新和删除系统中的任何文件(即所有权限)。

~ $ sudo su
# 输入密码后,即可以根用户身份执行命令
root@PCname:/home/usrname#
# 上行最后的" # " 就是根用户的符号

一般而言,sudo <cmd>将以根用户权限执行<cmd>
有一件事情是您必须作为根用户才能做的,那就是向 sysfs 文件写入内容。系统被挂载在 /sys 下,sysfs 文件则暴露了一些内核(kernel)参数。 因此,您不需要借助任何专用的工具,就可以轻松地在运行期间配置系统内核。注意 Windows 和 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

注意:管道|和重定向>< 是通过 shell 执行的,而不是被各个程序单独执行。

命令sudo echo 3 > brightness应分解为:1. shell (权限为您的当前用户) 尝试打开brightness文件(并删除原有文件中的内容);2. sudo echoecho程序以root权限,将参数 3 写入brightness文件。
然而,执行第一步时,shell没有root权限,无法打开brightness文件

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

$ echo 3 | sudo tee brightness

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

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

课后练习

  1. 本课程需要使用类Unix shell,例如 Bash 或 ZSH。如果您在 Linux 或者 MacOS 上面完成本课程的练习,则不需要做任何特殊的操作。如果您使用的是 Windows,则您不应该使用 cmd 或是 Powershell;您可以使用Windows Subsystem for Linux或者是 Linux 虚拟机。使用echo $SHELL命令可以查看您的 shell 是否满足要求。如果打印结果为/bin/bash/usr/bin/zsh则是可以的。

  2. /tmp 下新建一个名为 missing 的文件夹。

  3. man 查看程序 touch 的使用手册。

  4. touchmissing 文件夹中新建一个叫 semester 的文件。

  5. 将以下内容一行一行地写入 semester 文件:

    #!/bin/sh
    curl --head --silent https://missing.csail.mit.edu
    

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

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

  7. 查看 chmod 的手册(例如,使用 man chmod 命令)

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

  9. 使用 |> ,将 semester 文件输出的最后更改日期信息,写入主目录下的 last-modified.txt 的文件中

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

习题解答

  1. 在 /tmp 下新建一个名为 missing 的文件夹。

    ~ $ cd /tmp
    #  / 表示根目录,即:将当前工作目录切换到根目录下的tmp目录
    #  ~ 表示主目录,一般路径为:/home/usrname
    # 这里usrname是你为当前电脑用户所取的名字,同时也是home目录下的一个子目录
    /tmp $ mkdir missing
    /tmp $ ls
    
  2. 用 man 查看程序 touch 的使用手册。

    ~ $ man touch
    # 执行命令,会打开新的一个缓存页面,可以使用鼠标键盘上下浏览,或输入 q 退出
    

    一般来说,man命令返回的是一个冗长的手册(manual),如果想要快速了解一个命令的使用方法,可以尝试安装下面的包

    ~ $ sudo apt install tldr
    ~ $ sudo tldr -u
    # 更新数据库
    ~ $ sudo tldr touch 
    # 查询结果中,包含示例,可以直接复制后修改使用
    
  3. 用 touch 在 missing 文件夹中新建一个叫 semester 的文件。

    ~ $ cd /tmp/missing
    /tmp/missing $ touch semester
    /tmp/missing $ ls
    
  4. 将以下内容一行一行地写入 semester 文件:

    #!/bin/sh
    curl --head --silent https://missing.csail.mit.edu
    

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

    (shell中!表示事件指示符,即对历史列表中命令行条目的引用)

    ~ $ cd /tmp/missing
    /tmp/missing $ echo '#!/bin/sh' > semester
    /tmp/missing $ echo curl --head --silent https://missing.csail.mit.edu >> semester
    /tmp/missing $ cat semester
    
  5. 尝试执行这个文件。即,将该脚本的路径(./semester)输入到您的shell中并回车。如果程序无法执行,请使用 ls 命令来获取信息并理解其不能执行的原因。

    /tmp/missing $ ./semester
    bash: permission denied: ./semester
    /tmp/missing $ ls -l
    total 8
    -rw-r--r--  1   username    wheel 61 4 24 17:12 semester
    

    ls -l的返回结果,可知:当前用户对semester文件没有执行权限x

  6. 查看 chmod 的手册(例如,使用 man chmod 命令)

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

    /tmp/missing $ chmod 777 semester
    /tmp/missing $ ls -l
    total 8
    -rwxrwxrwx  1   username    wheel 61 4 24 17:12 semester
    /tmp/missing $ ./semester
    # 返回网址的head
    

    chmod允许使用数字代表权限,1为执行权限x2为只写权限w4为只读权限r7即为拥有三种权限,你也可以尝试chmod -x semester,再执行ls -l查看权限的变化。

    补充shebang一般写在脚本文件的开头,格式如下

    #! interpreter [optional-arg]
    

    其中,interpreter是指向一个可执行程序的路径(如windows中python的exe文件位置),后面的optional-arg是一个可选参数(可以不用写)
    shebang命令的作用是:在终端使用sourece semester./semester执行脚本semester时,告知bashzsh,需要调用哪个解释器(interpreter)来运行脚本。

  8. 使用 |> ,将 semester 文件输出的最后更改日期信息,写入主目录下的 last-modified.txt 的文件中.

    /tmp/missing $ ./semester | grep --ignore-case last-modified > ~/last-modified.txt
    /tmp/missing $ cat last-modified.txt
    last-modified: Sun, 23 Apr 2023 23:34:17 GMT
    
  9. 写一段命令来从 /sys 中获取笔记本的电量信息,或者台式机 CPU 的温度。注意:macOS 并没有 sysfs,所以 Mac 用户可以跳过这一题

    Windows 用户可以通过以下命令查询:

    ~$ cat /sys/class/power_supply/battery/capacity
    100
    

    Linux 用户可以通过以下命令查询:

    ~$ ls /sys/class/power_supply
    ADP0 BAT0 
    ~$ cat /sys/class/power_supply/BAT0/capacity
    100
    
    • 不同电脑使用的目录可能不同,例如ls命令结果:ACAD BAT1,或是其他类似结果
    • ACAD或ADP0:记录电源适配器相关的信息,如…/ADP0/online为1,表示处于充电状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值