MIT-Missing-Semester 课程笔记

简介

正如课程名字所言:“计算机教学中消失的一个学期”,这门课将会教会你许多大学的课堂上不会涉及但却对每个 CSer 无比重要的工具或者知识点。例如 Shell 编程、命令行配置、Git、Vim、tmux、ssh 等等,是迈向极客之路的奠基之课。

01 Course overview + the shell

课程讲义:Course overview + the shell · Missing Semester (mit.edu)

技巧

  • 在Shell中需要使用带空格的字符串(如创建文件夹时),可以使用单引号’('My Movie')或 双引号"("My Movie"),或者 \ (My\ Movie)
  • 使用xdg-open filename 会在系统中查找合适的程序打开文件,如xdg-open .会以图形化窗口的文件夹打开当前目录。
  • 使用xargs可以将STDIN作为命令的输入,如ls | xargs -d '\n' tar -cvf backup.tgz会将当前目录下的所有文件打包,并且可以打包文件名中含有空格的文件(使用 -d '\n'参数)

新知识

环境变量PATH

  • Shell会搜索PATH中的路径,找到用户在Shell中敲入的命令并执行它。
  • 可以使用 echo $PATH 来查看PATH中包含的路径,这些路径以:分隔
  • 可以修改PATH中的路径,从而修改Shell查找命令的路径

目录的权限

  • r:是否可以查看目录下的文件
  • w:是否可以修改目录下的文件名(包括删除该文件)
  • x:是否可以进入该目录

管道的权限

$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied

Permission denied的原因

  • | > < 这样的操作符,是由Shell执行的,而不是echo
  • 此处echo是具有root权限的,而Shell只有用户级权限
  • 执行顺序是,用户级的Shell使用>准备打开brightness文件,然后echo再进行写入
  • 因为用户级的Shell没有打开系统目录下brightness文件的权限,因此提示Permission denied

02 Shell Tools and Scripting

课程讲义:Shell Tools and Scripting · Missing Semester (mit.edu)

Shell脚本

Shell脚本中的 单引号'双引号"

单引号括起来的字符串中的变量不会展开(即被变量值替换),而双引号中的会展开

foo=bar
echo "$foo"
# prints bar
echo '$foo'
# prints $foo

Shell脚本的参数

  • $0:脚本名
  • $1~$9:脚本的第1~9个参数值
  • $@:所有的参数
  • $#:脚本的参数个数
  • $?:上一个命令的返回值
  • $$:当前脚本的PID
  • !!:上一个命令
  • $_:上一个命令的的最后一个参数

程序的返回状态

  • 程序执行成功,返回0;执行失败,返回0以外的错误码
  • 可以对程序的返回状态作逻辑运算,但需要注意的是,逻辑运算不是以程序的返回值,而是以程序是否成功执行来进行运算的。即执行成功时返回的0表示true;执行失败时返回的1表示false。
  • Linux内置truefalse两个程序:true固定返回程序执行成功时的返回值0,false固定返回程序执行失败时的返回值1。可以通过执行 truefalse,然后执行 echo $? 来查看这个两个的程序的运行结果
  • 逻辑运算 &&||是短路运算符,即只要前面的程序执行状态能确定最终的逻辑运算结果,就不执行后面的程序
false || echo "Oops, fail"
# Oops, fail
true || echo "Will not be printed"
#
true && echo "Things went well"
# Things went well
false && echo "Will not be printed"
#
true ; echo "This will always run"
# This will always run
false ; echo "This will always run"
# This will always run

获取命令的输出

命令替换

$( CMD )会直接将命令的输出替换到使用它的地方;如遍历当前目录下的内容 for file in $( ls )

进程替换

<( CMD )会将命令的输出保存到临时文件中,再将临时文件中的内容输入重定向给使用它的地方;适用于想要从文件中获取值而非从STDIN中获取值的情况。如 diff <( ls foo ) <( ls bar )会比较foo和bar文件夹下文件的异同

{}的扩展

{}中可以放入一系类的值,这些值会在执行时自动扩展。如:

convert image.{png,jpg}
# Will expand to
convert image.png image.jpg
mkdir foo bar
# This creates files foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h
touch {foo,bar}/{a..h}
touch foo/x bar/y
# Show differences between files in foo and bar
diff <(ls foo) <(ls bar)
# Outputs
# < x
# ---
# > y

Shebang Line

  • 格式:由 #!开头,并构成字符序列 #! xx/xx/x
  • 位置:通常位于脚本文件的第一行
  • 功能:在程序运行的时候,让程序载入器将#!后面的内容,作为解释器指令,并调用该指令
  • 问题:需要再写脚本时指定解释器在计算机中的路径,如果在其他电脑上运行该脚本,则可能因为找不到解释器而无法执行脚本,换言之,其可移植性不高
  • 提高可移植性的办法:使用env命令,该命令会在PATH中查找解释器路径,并直接调用对应的解释器。如#!/usr/bin/env python

Shell脚本综合示例

#!/bin/bash
# 1. iterate through the arguments provide
# 2. grep for the string foobar
# 3. append it to the file as a comment if it’s not found
echo "Starting program at $(date)" # Date will be substituted
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"; do
    grep foobar "$file" > /dev/null 2> /dev/null
    # When pattern is not found, grep has exit status 1
    # We redirect STDOUT and STDERR(用2表示) to a null register since we do not care about them
    if [[ $? -ne 0 ]]; then
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done

Shell函数的编写

格式
[ function ] funname [()]
{
    action;
    [return int;]
}
使用
  • 可以直接在Shell命令行中输入function func()来创建函数并直接调用,如
system@ubuntu:~$ function test()
> {
>     echo "Hello World"
> }
system@ubuntu:~$ test
Hello World
  • 可以在Shell脚本中编写函数,使用source命令载入(重载)包含该函数的脚本文件后,即可在Shell中调用该函数

函数 VS 脚本

对比点函数脚本备注
编程语言只能是同一编程语言可以由不同的编程语言编写对于脚本而言Shebang Line很重要,因为它指示了该脚本的编程语言解释器位置
加载一旦定义被读取就加载每次被执行时加载1. 函数比脚本加载得稍微快一些
2. 每次修改函数,都需要重新加载其定义
运行环境运行在当前的Shell环境中在自己的进程中执行(会创建自己的进程)1. 函数能修改环境变量,如修改当前工作目录
2. 脚本只能通过export导出环境变量的值

Shell工具

命令帮助文档

tldr:列出该命令的常用参数使用方式,而不会像man--help一样对每个参数做大量的说明

查找文件

递归查找符合标准的文件
# Find all directories named src
find . -name src -type d
# Find all python files that have a folder named test in their path
find . -path '*/test/*.py' -type f
# Find all files modified in the last day
find . -mtime -1
# Find all zip files with size in range 500k to 10M
find . -size +500k -size -10M -name '*.tar.gz'
对查找到的文件做指定操作
# Delete all files with .tmp extension
find . -name '*.tmp' -exec rm {} \;
# Find all PNG files and convert them to JPG
find . -name '*.png' -exec convert {} {}.jpg \;
find命令的替代命令

fd命令

  • 提供颜色化的输出、默认正则匹配、Unicode支持等
  • 使用简单、快速、用户友好,如搜索文件时只需要 fd pattern

locate命令

  • 建立以文件名为索引的数据库进行文件查找
  • 使用updatedb更新数据库
  • find、fd等命令可以使用如修改日期、文件大小、文件权限等属性进行查找,而locate只能基于文件名进行查找

文本查找grep命令

语法
grep [options] pattern [files]
  • pattern - 表示要查找的字符串或正则表达式
  • files - 表示要查找的文件名,可以同时查找多个文件,如果省略 files 参数,则默认从标准输入中读取数据
常用参数
  • -i:忽略大小写进行匹配。
  • -v:反向查找,只打印不匹配的行。
  • -n:显示匹配行的行号。
  • -r:递归查找子目录中的文件。
  • -l:只打印匹配的文件名。
  • -c:只打印匹配的行数。

03 Editors (Vim)

课程讲义:Editors (Vim) · Missing Semester (mit.edu)

参考文档:vim的搜索与替换 - 知乎 (zhihu.com)

学习新编辑器的方法

  1. 从一个教程开始
  2. 使用这个编辑器完成所有的文字编辑工作
  3. 不断精进,寻求更好更高效的处理方式

Vim基础

Buffers, Tabs, and Windows

  • Buffers:VIM打开的一系列文件
  • Tabs:标签页
  • Windows:基本上就是一个阅读用的窗口
三者之间的关系

一个Buffer可能会在多个Windows中打开,而这多个Windows又处于同一个Tab下

VIM指令

  • :e {name of file}:打开文件并编辑
  • :ls:显示打开的buffers
  • :help {topic}:打开帮助文档
    • :help :w:打开:w的帮助文档
    • :help w:打开w动作的帮助文档

VIM分屏

  • :sp {name of file}:水平分屏打开新文件
  • :vsp {name of file}:竖直分屏打开新文件
  • Ctrl + w + w:切换分屏
  • Ctrl + w + N + >:向右加宽N
  • Ctrl + w + N + <:向左加宽N
  • :only:仅保留当前分屏
  • :hide:关闭当前分屏
  • :quit:退出当前分屏

VIM标签页

  • :tabnew:打开新的标签页,可以在该标签页下输入命令以创建或打开文件
  • :tabfind {name of file}:搜索并在新标签页中打开文件
  • :tabs:显示已打开标签页的列表
  • :tabclose:关闭当前标签页
  • :tabonly:仅保留当前标签页
  • :tabn/gt:切换到下一标签页
  • :tabp/gT:切换到上一标签页
  • :tabm {index}:将当前标签页放置到指定位置,index从0开始
  • :tabfirst:切换到第一个标签页
  • :tablast:切换到最后一个标签页

VIM交互

移动

  • 单词
    • w:下一个单词
    • b:单词的开头
    • e:单词的结尾
    • 0:行首
    • $:行尾
    • ^:第一个非空字符
  • 屏幕
    • H:光标移动到这个屏幕的最上方那一行的第一个字符
    • M:光标移动到这个屏幕的中央那一行的第一个字符
    • L:光标移动到这个屏幕的最下方那一行的第一个字符
  • 滚动
    • Ctrl + f:屏幕向下滚动一页
    • Ctrl + b:屏幕向上滚动一页
    • Ctrl + d:屏幕向下滚动半页
    • Ctrl + u:屏幕向上滚动半页
  • 跳转% 跳转到匹配的另一个括号
  • 搜索
    • /{regex}:可以使用正则表达式进行搜索
    • n:下一个符合的匹配项
    • N:上一个符合的匹配项
  • 替换
    • %s/foo/bar/g:将文件中所有的foo替换为bar
    • line_a,line_bs/foo/bar/g:将line_a到line_b范围内的foo替换为bar
  • 复制
    • y:在当前文件中复制
    • "+y:跨文件复制

编辑符

与动作搭配使用的编辑符i表示内部,a表示周遭

  • ci(:改变当前()内部的内容,不包括(
  • da':删除'的内容,包括'

VIM配置

配置文件

  • Linux环境下编辑(创建) ~/.vimrc文件
  • Windows环境下在VIM的安装目录下,编辑(创建) _vimrc文件

常见配置

" 设置行号
set number
" 显示相对行号
set relativenumber
" 语法高亮
syntax enable
" 设置gvim的字体
set guifont=Consolas:h12
" 设置自动缩进
set autoindent
" 按下Tab键后,vim显示的空格数 
set tabstop=4
" Normal模式下,缩进的空格数
" >> 增加一级缩进
" << 取消一级缩进
" == 取消全部缩进
set shiftwidth=4
" 自动将Tab转为空格
set expandtab
" 一个Tab转为多少空格
set softtabstop=4
" 高亮光标所在行
set cursorline
" 设置高亮号颜色
" Vim识别三种不同的终端:term,黑白终端;cterm,彩色终端;gui,Gvim窗口
" (c)term,可以定义其字体显示为:bold、underline、reverse、italic或standout,用逗号可以组合使用这些属性
" ctermfg设置前景色,ctermbg设置背景色
hi CursorLine cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white
" 自动高亮另一半括号
set showmatch
" 在底部显示当前键入的命令
set showcmd
" 高亮搜索结果
set hlsearch
" 命令模式下,底部操作指令按下Tab键自动补全
" 第一次按下Tab,显示所有匹配的操作指令
" 第二次按下Tab,会依次选择各个指令
set wildmenu
set wildmode=longest:list,full
" 设置文件的编码格式
set encoding=utf-8
set termencoding=utf-8
set fileencoding=utf-8
set fileencodings=ucs-bom,utf-8,chinese,cp936
" 支持在Visual模式下,通过C-y复制到系统剪切板
vnoremap <C-y> "+y
" 支持在normal模式下,通过C-p粘贴系统剪切板
nnoremap <C-p> "*p

在其他软件中使用VIM模式

Shell

  • Bash:set -o vi
  • Zsh:bindkey -v
  • Fish:fish_vi_key_bindings

ReadLine

对于使用GUN Readline的软件,如Python REPL。
~/.inputrc文件中添加 set editing-mode vi

04 Data Wrangling

课程讲义:Data Wrangling · Missing Semester (mit.edu)

参考资料:Linux文本三剑客超详细教程—grep、sed、awk - alonghub - 博客园 (cnblogs.com)

正则表达式

数据挖掘

grep

简介
  • 全称:Global Regular Expression Print
  • 功能:能使用正则表达式搜索文本,并把匹配的行打印出来(匹配到的标红)
  • 工作方式:
    • 在一个或多个文件中搜索字符串模板。
    • 如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。
    • 搜索的结果被送到标准输出,不影响原文件内容。
  • 返回值
返回值说明
0搜索成功
1搜索失败
2搜索的文件不存在
命令格式
grep [option] pattern file
命令参数
  • -A<显示行数>:除了显示符合范本样式的那一列之外,并显示该行之后的内容。
  • -B<显示行数>:除了显示符合样式的那一行之外,并显示该行之前的内容。
  • -C<显示行数>:除了显示符合样式的那一行之外,并显示该行之前后的内容。
  • -c:统计匹配的行数
  • -e :实现多个pattern间的逻辑or关系
  • -E:扩展的正则表达式
  • -f FILE:从FILE获取PATTERN匹配
  • -F :相当于fgrep
  • -i --ignore-case #忽略字符大小写的差别。
  • -n:显示匹配的行号
  • -o:仅显示匹配到的字符串
  • -q: 静默模式,不输出任何信息
  • -s:不显示错误信息。
  • -v:显示不被pattern 匹配到的行,相当于[^] 反向匹配
  • -w :匹配 整个单词

sed

简介
  • sed是一个“流编辑器”,一次处理一行内容
  • 处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(patternspace )
  • 用sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。然后读入下行,执行下一个循环,不断重复,直到文件末尾。文件内容并没有改变,除非使用重定向存储输出或-i
功能

用来自动编辑一个或多个文件, 简化对文件的反复操作

命令格式
sed [options] '[地址定届] command' file(s)
常用options
  • -n:不输出模式空间内容到屏幕,即不自动打印,只打印匹配到的行
  • -e:多点编辑,对每行处理时,可以有多个Script
  • -f:把Script写到文件当中,在执行sed时-f 指定文件路径,如果是多个Script,换行写
  • -r:支持扩展的正则表达式
  • -i:直接将处理的结果写入文件
  • -i.bak:在将处理的结果写入文件之前备份一份
地址定界
  • 不给地址:对全文进行处理
  • 单地址
    • #(行号):指定的行
    • /pattern/:被pattern所能匹配到的每一行
  • 地址范围
    • #1,#2:从第#1行到第#2行
    • /pattern1/, /pattern2/:从第一个模式到第二个模式
    • #(行号) , /pattern/:从第#行到匹配到的模式
  • ~:步进
    • sed -n '1~2p' :从第一行开始,一次加两行;即只打印奇数行
    • sed -n '2~2p':从第二行开始,一次加两行;即只打印偶数行
编辑命令command
  • d删除模式空间匹配的行,并立即启用下一轮循环
  • p打印当前模式空间内容,追加到默认输出之后
  • a:在指定行后追加文本,支持使用\n实现多行追加
  • i:在行前面插入文本,支持使用\n实现多行插入
  • c替换行为单行或多行文本,支持使用\n实现多行替换
  • w:保存模式匹配的行到指定文件
  • r:读取指定文件的文本到模式空间中匹配的行后
  • =:为模式空间中的行打印行号
  • !:模式空间中匹配的行取反处理
  • s///参数查找并替换,以下是常用参数,参数添加在末尾
    • g:全局行内替换
    • l:下个字符转换成小写
    • L:全部匹配到的文本替换为小写
    • u:下个字符转换为大写
    • U:全部匹配到的文本替换为大写
    • E:停止大小写转换

05 Command-line Environment

课程讲义:Command-line Environment

参考文献

Job Control

Killing a process

Signal

Unix系统用于进程间交流信息,使用man 7 signal可以查看对于各个信号的详细说明

发送信号
  • SIGINT:Ctrl + C
  • SIGQUIT:Ctrl + \
  • kill -SIGNAL(或Signal ID) PID:kill命令可以指定发送特定信号,默认发送SIGTREM
    • kill -l:显示kill所能发送的信号列表
  • SIGHUP:关闭终端或关闭远程连接时发送,会导致进程挂起,即不再运行
    • nohup:在命令前添加,不响应SIGHUP信号
    • disown:在命令已经在运行时使用,不响应SIGHUP信号
      • 语法:disown [参数] [标识符or进程ID]
      • -h标记每个作业标识符,这些作业将不会在shell接收到sighup信号时接收到sighup信号
      • -a:移除所有的作业
      • -r:移除运行的作业
  • SIGKILL:立即终止进程,不可被进程捕获;缺点在于会留下孤儿进程

进程管理

  • Ctrl + ZSIGSTOP:暂停进程

  • fg:在前台继续进程

  • bg:在后台继续进程

  • commands &:使命令在后台运行,但仍会占用Shell的STDOUT,这种情况下可以使用Shell的重定向解决

  • jobs:查看当前命令行会话正在运行的进程

    • jobs 会为进程分配作业编号,通过%作业编号可以指定进程
    • jobs -l可以显示进程的pid

终端复用器(Terminal Multiplexers)

优点

  • 与多个Shell会话交互
  • 与当前的Shell会话断开,在后续某个时间点恢复会话,会话中的命令无需使用nohup,因此在远程连接时尤为有用

tmux

近来较为流行的终端复用器工具,除此之外还有screen工具。

tmux的相关概念以及对应的快捷键如下:

会话(Sessions)

带有一到多个窗口的、独立的工作空间

  • tmux:开启一个新的会话
  • tmux new -s NAME:开启一个新会话并为其命名
  • tmux ls:列出当前会话
  • tmux内,使用<Ctrl + B> d断开当前会话
  • tmux -a:恢复最后一个会话
  • tmux -t FLAG:恢复指定会话
视窗(Windows)

类似浏览器的标签页(Tabs),是同一会话的不同可视部分

  • <Ctrl + B> c:创建一个新的视窗。使用Ctrl + D关闭
  • <Ctrl + B> N:跳转到第N个视窗
  • <Ctrl + B> n:跳转到下一个视窗
  • <Ctrl + B> p:跳转到上一个视窗
  • <Ctrl + B> , :重命名当前视窗
  • <Ctrl + B> w:列出当前视窗
面板(Panes)

类似Vim的分屏

  • <Ctrl + B> ":水平分屏
  • <Ctrl + B> %:垂直分屏
  • <Ctrl + B> 方向键:使用键盘上的方向键向指定方向移动面板
  • <Ctrl + B> z:将当前面板扩大到整个屏幕

Dotfiles

别名(alias命令)

alias可以为常用的 命令 + 选项 + 参数 创建一个简短、自定义的别名,从而能够提高我们输入、使用命令的效率。

alias命令在Shell中创建的别名在Shell关闭后就会消失,如果想要将这些别名永久化,需要写入到配置文件中

常用
alias gs="git satus"
alias ga="git add ."
alias gc="git commit -m"
alias gp="git pull"
其他
  • unalias alias_name:禁用某个别名
  • alias alias_name:查看某个别名的定义

配置文件

常见配置文件路径
  • bash~/.bashrc, ~/.bash_profile
  • git~/.gitconfig
  • vim~/.vimrc, ~/.vim文件夹
  • ssh~/.ssh/config
  • tmux~/.tmux.conf
配置文件的管理

使用版本控制工具管理配置文件,通过脚本将配置文件链接到对应的文件夹下。

具有以下好处:

  • 易于安装
  • 便携
  • 多端同步
  • 变更追踪
配置文件脚本
# 判断Shell类型,特定类型的Shell可能具有某些特性
if [[ "$SHELL" == "zsh" ]];
then
	{do something};
fi

# 针对特定主机进行个性化定制
if [[ "$(hostname)" == "myServer" ]];
then
	{do something};
fi

# 引用外部的文件
if [ -f ~/.alias ];
then
	source ~/.alias
fi

Remote Machines

连接

ssh victor@bar.mit.edu
  • 用户名:victor

  • 服务器:bar.mit.edu,或IP地址

执行命令

ssh victor@server ls | grep PATTERN

在远程执行ls命令,在本地执行grep命令

ls | ssh victor@server grep PATTERN

在本地执行ls命令,在远程执行grep命令

SSH密钥

使用密钥对进行授权,服务器持有公钥,用户主机持有私钥,通过使用SSH密钥可以避免在每次连接服务器时都输入密码的麻烦。

生成密钥
ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519

密钥授权

面向服务器进行密钥授权,Gitee或Github的密钥授权详解官方网站

ssh通过查看.ssh/authorized_keys来决定允许哪台主机接入,因此可以复制公钥到指定路径

cat .ssh/id_ed25519.pub | ssh foobar@remote 'cat >> ~/.ssh/authorized_keys'

或者可以使用更简单的命令ssh-copy-id

ssh-copy-id -i .ssh/id_ed25519 foobar@remote

从SSH复制文件

  • ssh + tee:使用ssh执行命令,并通过teeSTDIN的内容写入到文件中。

    cat localfile | ssh remote_server tee serverfile
    
  • scp:可以用于递归复制大量的文件和文件夹

    scp path/to/local_file remote_host:path/to/remote_file
    
  • rsync:在scp的基础中做了改进

    • 可以检测相同文件,对于相同文件不会进行复制
    • --partial:支持断点续传
    • 使用语法与scp类似

端口转发

介绍

每个程序或者应用都会运行在一个指定的端口下(port), 我们可以直接通过监听(listen)该端口来查看其他用户向该应用发出的请求(request)并做出响应(response)。

本地转发(Local port forwarding):发送给本地端口的请求转发到远程机器上

远程转发(Remote port forwarding):远程机器监听请求并将请求转发给本地机器来做出响应

转发命令的常用参数
  • -N:这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道。
  • -f:后台运行SSH隧道,即使关闭创建隧道时所使用的SSH会话,对应的SSH隧道也不会消失。
本地转发
说明

本地转发示意图

目标主机是SSH终点机器:此处的==localhost并不是本地主机,而是远程主机的localhost==

$ ssh -L 123:localhost:456 remote-host

目标主机并不是SSH终点机器,而是当作跳板机

$ ssh -L 123:target-host:456 remote-host
应用

1. 使用性能更好的远程服务器运行代码,在本地进行操作

$ ssh -L 9999:localhost:8888 user@remote_server
  • user@remote_server是指定服务器的账户
  • 可以在本机上进入localhost:9999来进行代码的编写,而运行任务会转发给远程服务器

2.VPN

$ ssh -L 8080:internal-server:80 -L 8443:internal-server:443 bastion-host -N
  • 某个内网的服务器,端口号为80,443
  • 本机端口号为8080,8443
  • 内网的服务器无法从外网直接访问,但如果网络管理者为我们开放了一个bastion host提供ssh服务,那么就可以借助它作为跳板机访问内部服务器
远程转发

在这里插入图片描述

应用
$ ssh -R 2080:target-server:80 local-host
  • 本地主机处于外网,而目标机器或者目标机器与SSH的跳板机均位于内网中
  • 只能反过来通过SSH跳板机来发起隧道,即让远端的机器(我们的本地主机)来进行端口转发
  • 本地主机端口为2080,内网中的服务端口为80
  • 此时,本地机器lcoal-host作为ssh的服务端,必须要安装SSH服务器,才能接受跳板机的远程登录。

SSH配置文件

服务器端/etc/ssh/sshd_config

客户端~/.ssh/config

SSH配置文件除了可供ssh使用外,还可以供scprsync等工具使用

Host vm
    User foobar
    HostName 172.16.174.141
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 9999 localhost:8888

# 配置文件可以使用通配符
Host *.mit.edu
    User foobaz

扩展

mosh:移动端的ssh,在scp的基础上做了改进,修复了bug。目前做到了全平台覆盖。

sshfs:可以将远程服务器的文件夹挂载到本地,然后就可以进行本地操作

Shell相关杂项

zsh

zsh 是一个兼容 bash 的 shell,相较 bash 具有以下优点:

  • Tab 补全功能强大。命令、命令参数、文件路径均可以补全。
  • 插件丰富。快速输入以前使用过的命令、快速跳转文件夹、显示系统负载这些都可以通过插件实现。
  • 主题丰富。
  • 可定制性高。

框架

类似主题,但需要注意的是,使用大量框架可能导致Shell启动和运行的速度变慢,因此需要进行取舍权衡。

zsh相关的框架有:

终端仿真器

终端仿真器是用于运行Shell的工具,不同的Linux/Unix发行版使用不同的终端仿真器,对所使用的终端仿真器进行选择和个性化配置,可以有效提高开发效率。在选择及配置时应作如下考虑:

  • 字体
  • 配色方案
  • 快捷键
  • 标签页/面板 支持
  • 回滚设置
  • 性能:某些新型终端仿真器,如Alacrittykitty提供GPU加速

06 Version Control(Git)

课程笔记:Version Control (Git)

参考文档

Git的数据模型

快照(SnapShots)

  • 快照:Git通过一系列的快照来保存文件和文件夹的历史集合,快照是最顶层的追踪记录单元。
  • blob:文件
  • tree:文件夹;tree 可以将blobtrees与其名字相映射,即tree可以包含其他的tree以及blob

快照示例

<root> (tree)
|
+- foo (tree)
|  |
|  + bar.txt (blob, contents = "hello world")
|
+- baz.txt (blob, contents = "git is wonderful")

修改历史(快照之间的关系)

  • Git的历史记录是一个有向无环图
  • Git的一个快照可能继承自多个父快照,即由多个平行的分支merge而成

快照关系图

o <-- o <-- o <-- o <---- o
            ^            /
             \          v
              --- o <-- o

数据模型伪代码

// a file is a bunch of bytes
type blob = array<byte>

// a directory contains named files and directories
type tree = map<string, tree | blob>

// a commit has parents, metadata, and the top-level tree
type commit = struct {
    parents: array<commit>
    author: string
    message: string
    snapshot: tree
}

Git的存储结构

对象和内容追踪

对象

treeblob或者commit(快照)

type object = blob | tree | commit
内容关联

Git的数据存储中,所有object都是通过SHA1- hash算法来进行内容关联的。

objects = map<string, object>

def store(object):
    id = sha1(object)
    objects[id] = object

def load(id):
    return objects[id]
  • Git的object通过 哈希值 关联它们所引用的对象,而不是直接包含它们在磁盘上的内容。
  • 通过git cat-file -p HASH_VALUE即可查看对应的哈希值所关联的内容。

引用

快照对应的哈希值有40位,并不便于人类进行记忆,因此Git提供了便于阅读的、对特定快照的引用。

通过使用引用,可以设置指定快照继承哪些父快照。常用的引用如下:

  • HEAD:当前所在的快照
  • master:最新的快照

暂存区

Git的快照并不是保存当前工作目录的状态,而是使用了暂存区,通过add选项,可以指定需要将哪些修改保存到下一个快照中,而不是对当前所有修改照单全收。这种处理方式允许我们在开发的时候有更多的选择空间,可以更灵活的进行开发作业。

Git命令

基础使用

  • git help <command>:获取Git命令的说明帮助
  • git init:初始化Git仓库,数据存储在.git文件夹中
  • git status:获取当前状态,如未进行追踪的修改等信息
  • git add <filename>:添加指定文件的修改到暂存区
  • git commit:创建一个新的快照
  • git log:显示commit历史,并携带哈希值等信息
  • git log --all --graph --decorate:可视化显示历史记录(以有向无环图的形式)
  • git diff <filename>:显示相对于暂存区的变更内容
  • git diff <revision> <filename>:显示相对于指定快照的变更内容
  • git checkout <revision>:改变HEAD和当前分支

分支与合并

  • git branch:列出当前所有分支
  • git branch <name>:创建新分支
  • git checkout -b <name>:创建新分支并切换到该分支,等同于git branch <name>; git checkout <name>
  • git merge <revision>:将指定分支与当前分支合并
  • git mergetool:解决合并时的冲突问题

远程

  • git remote:列出所有连接的远程仓库
  • git remote add <name> <url>:添加远程仓库
  • git push <remote> <local branch>:<remote branch>:提交本地分支的代码到远程仓库指定分支
  • git branch --set-upstream-to=<remote>/<remote branch>:关联本地分支和远程分支
  • git fetch:拉取最新的Git仓库内容,但不改变HEAD指向的快照
  • git pull:拉取最新的Git仓库内容,并将HEAD指向最新的快照
  • git clone:从远程仓库下载Git仓库

撤销

  • git commit --amend:修改快照的内容
  • git reset HEAD <file>:取消对某个文件在暂存区的修改
  • git checkout -- <file>:丢弃修改

高级

  • git config:Git的配置信息
  • git clone --depth=1:指定克隆的层数,而不是下载整个仓库
  • git add -p:交互式的暂存
  • git blame:显示某一行最后由谁进行修改
  • .gitignore:记录不进行追踪的文件,如生成的可执行文件等。

## 添加公钥

> 参考链接:https://cloud.tencent.com/developer/article/1594769

在使用如Gitee、Github等在线代码托管网站的时候,如果是在Windows系统上,还可以通过git配置文件,配置用户名和密码,避免每次下载和上传都要输入的麻烦,可是在Linux系统上,则需要配置SSH密钥才可以。

### 生成密钥

```bash
ssh-keygen -t rsa -C "xxxx@xxx.com"

添加公钥

ssh会生成公钥和私钥,公钥需要部署到gitee等远程仓库的网站上。使用cat查看公钥(以.pub结尾),将内容复制到gitee等网站提供的ssh密钥页面,添加密钥即可。

cat ~/.ssh/debian11_rsa.pub

添加远程仓库

# 或git@github.com
ssh -T git@gitee.com

添加私钥

我在 添加远程仓库上传文件 的时候,会遇到git@gitee.com: permission denied(publickey)的报错信息,这是由于没有在本地ssh中添加密钥的缘故。

ssh默认会添加如id_rsa等名称的密钥,但我自己命名的密钥名为debian11_rsa,ssh无法搜索到连接gitee所需的密钥,因此会出现无法添加远程仓库的问题,所以此处需要手动添加密钥。

# ssh-add后接生成密钥时选择的密钥路径
ssh-add ~/.ssh/debian_rsa

学习资源

07 Debugging and Profiling

课程讲义:Debugging and Profiling

参考资料:

Debugging

通过printf进行Debugging和Logging

  • 通过在代码中添加printf语句,可以获取自己需要的程序执行信息,从而查明问题发生的原因
  • 相比于简单的添加printf语句,使用log机制具有以下好处:
    • 可以将log输出到文件、端口甚至远程服务器,而不只是标准输出STDOUT
    • 可以对log进行等级设置,并根据等级对log进行过滤,获取自己需要的信息;常见的log等级有:INFO, DEBUG, WARN, ERROR等
Log编写示例

设置了Log等级、Log颜色的Python脚本文件:logger.py

$ python logger.py
# Raw output as with just prints
$ python logger.py log
# Log formatted output
$ python logger.py log ERROR
# Print only ERROR levels and above
$ python logger.py color
# Color formatted output
给不同等级的Log添加颜色

在Linux操作系统中,可以通过使用 ANSI转义代码 来对Log进行颜色设置

# 这里[255;0;0]是所要表示颜色的RGB值
echo -e "\e[38;2;255;0;0mThis is red\e[0m"

# 在shell中打印所有的颜色
#!/usr/bin/env bash
for R in $(seq 0 20 255); do
    for G in $(seq 0 20 255); do
        for B in $(seq 0 20 255); do
            printf "\e[38;2;${R};${G};${B}m█\e[0m";
        done
    done
done

第三方Log

  • Log保存路径:/var/log
  • 系统Log:通过守护进程systemd进行服务控制
    • 保存路径:/var/log/journal
    • 查看命令:journalctl
  • 内核Log:通过dmesg命令查看
  • lnav是一个高级的、便于进行小规模log查看的工具

Debug工具

  • pdb:Python自带的Debug工具
  • gdb
    • 不仅可以对C风格的编程语言进行调试,而且可以对任何进程进行堆、栈、寄存器等信息的查看
    • 可以安装pwndbg来增强gdb的功能

特殊工具

系统调用

通过strace命令可以跟踪程序进行的系统调用

网络数据包

对网络数据包的分析可以使用以下工具:

  • tcpdump
  • Wireshark
静态检查工具

分析(Profilling)

耗时(Timing)

  • 实时(Real):从程序开始到结束所耗费的时间,包括其他进程所耗费的时间以及等待时(如等待I/O、网络)所耗费的时间
  • 用户(User):CPU运行用户代码所耗费的时间
  • 系统(Sys):CPU运行内核代码所耗费的时间

一般而言,用户 加上 系统 即为程序运行所真正耗费的时间;运行程序时在开头添加time即可查看程序运行所耗费的时间。

CPU

CPU分析器分类
  • 跟踪分析器(Tracing Profiler):记录程序中每次函数调用
  • 取样分析器(Sampling Profiler):定期(通常是ms级)探测程序运行情况,并记录堆栈信息
命令行工具(Python为例)
  • 使用cProfile模组,分析每次函数调用的耗时

    $ python -m cProfile -s tottime SCRIPT.py
    
  • 使用行分析(line Profiler)工具,分析每行代码的耗时。cProfile的缺点在于,对于使用的第三方库函数,也会记录耗时,从而导致输出的结果非常冗长,难以获取需要的数据,而使用行分析工具则没有这种苦恼。

    $ kernprof -l -v SCRIPT.py
    

内存

对于C/C++而言,未释放的内存就无法再继续使用;而对于拥有内存回收机制的Python而言,通过通过指针申请分配的内存也不会被释放。因此通过内存分析工具分析内存泄露问题是很有必要的。

  • Valgrind:C/C++等语言的内存分析工具

  • memory_profiler:Python用于内存分析的模组

    $ python -m memory_profiler SCRIPT.py
    

事件分析

不是通过对耗时或者内存进行分析,而是对与该程序运行相关的事件进行分析,如页错误数(page faults)等信息。可以使用perf命令来对程序进行事件分析:

  • perf list:列出perf能追踪的所有事件
  • perf stat COMMAND ARG1 ARG2:获取与进程或命令相关的不同事件数量
  • perf record COMMAND ARG1 ARG2:记录命令运行情况,并将统计结果保存到perf.data文件中
  • perf report:格式化打印perf.data中收集到的数据

可视化

火焰图

将统计数据以火焰图的形式进行展示,包括但不限于CPU使用情况、内存占用情况等。如Y轴表示函数调用等级、X轴表示函数调用耗时所生成的图片如下所示:

在这里插入图片描述

函数调用图

在Python中可以使用pycallgraph来绘制函数调用图

资源监测

常规监测
  • htoptop命令的升级版。
    • <F6>:进程排序
    • t:树状图显示调用等级
    • h:切换线程
  • glances:拥有更好看的UI
  • dstat:对所有进程的信息进行统计,能对许多子系统如I/O系统,网络系统的实时资源占用率进行计算。
I/O操作

iotop能实时显示I/O占用情况,并且能够检查进行重度I/O操作的进程

磁盘使用
  • du:显示当前目录下每个文件的磁盘占用情况
  • df:显示每个分区的磁盘占用情况
  • ncdu:提供交互式的方式提供目录导航(切换目录)、删除文件或目录等操作
内存使用

free显示系统已用和空闲的内存大小

打开文件

lsof列出打开(占用)文件的进程信息

网络连接和设置
  • ss:一个机器上的特定端口被哪个进程占用
  • ip:显示路由、网络设备、接口
网络使用
  • nethogs
  • iftop

特殊工具

  • stress:测试某个工具的抗压能力
  • hyperfine:比较两个不同程序的执行效率,如搜索的快慢

08 MetaProgramming

课程讲义:Metaprogramming

参考资料

编译系统(Build System)

原理

定义一系列的目标依赖 以及如何生成这些目标依赖规则,编译系统会检测生成目标所需的依赖,并应用规则生成这些依赖;而依赖可能会有其他依赖,因此依赖也可能是一个目标;通过这样的递归过程,编译系统能生成最终目标。

理想的编译系统能避免不必要的规则执行过程,如某些依赖并未发生改变,因此并不需要据此生成新的文件。

Make

最常见的编译系统工具是make,会自动执行当前目录下命名为makefile的文件,其使用方式如下:

paper.pdf: paper.tex plot-data.png
	pdflatex paper.tex

plot-%.png: %.dat plot.py
	./plot.py -i $*.dat -o $@
  • 冒号左边的是要生成的目标文件,如paper.pdf、plot-%.png

  • 冒号右边的是生成目标文件所需的依赖文件,如paper.tex、plot-data.png

  • 被缩进的内容是为生成目标文件所执行的指令

  • %是通配符,在冒号前后一致,指代同一个字符

  • 第一行的是最终目标文件,为添加任何参数时,make会生成最终目标文件;但如果使用make plot-data.png这样的指令,则能指定生成的目标文件

依赖管理

语义化版本

使用语义化版本(semantic versioning)标准,将程序的版本号命名为:major.minor.patch ,有助于进行依赖管理。开发人员借此可以知晓哪些版本可以放心使用,而哪些版本或许不能正常使用。

  • patch:新的发布没有影响到API
  • minor:添加了可以向后兼容的API
  • major:通过不支持向后兼容的方式修改了API

lock files

lock files 会列出当前所有依赖项的准确版本。

通常开发人员需要准确的运行升级程序来对依赖项进行升级,而采用lock files的好处在于:

  • 避免不必要的重复编译
  • 可重复构建,即在对外发布的时候,可以通过同一套代码构建出一致的软件包
  • 避免自动升级到最新版本,这可能会导致程序崩溃

持续构建系统(Continuous integration systems/CI)

CI系统的主要作用在于,当代码发生改变时,自动相应的动作。如格式检测、推送到Pip仓库等等。

常见的CI系统有:Travis CI、Azure Pipelines、GitHub Actions

09 Security and Cryptography

课程讲义:Security and Cryptography

参考资料

  • 熵通过bits来衡量,用于表示从一系列可能性中随机选取的概率。熵的计算公式为:log~2~(可能性的数量)
  • 攻击者知道的是密码的模式,但并不知道随机选择密码的随机性。可以通过物理手段,如掷骰子来进行随机选择字典中的某一项来使得随机性最大,而不是个人凭直观的选择。因为人类并不能做到完全的随机,很多时候的选择是有迹可循的,而这点可能会被攻击者利用。
  • 密码的熵为多少比较合适呢?一般而言熵为40左右的密码比较好;但如果要预防离线攻击,80左右才比较合适。

哈希函数

哈希函数可以将任意大小的输入转化为固定长度的输出,例如Git使用的SHA1函数,可以将任意长度的输入转化为160bit(40个16进制数),可以使用sha1sum命令调用SHA1函数。

$ printf 'hello' | sha1sum
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

(加密用)哈希函数一般应该具有以下特点:

  • 决定性(Deterministic):相同的输入会生成相同的输出
  • 不可逆性(Non-invertible):难以通过所需的输出倒推寻找到其输入
  • 目标冲突抵抗性(Target collision resistant):对于指定的输入m_1,难以找到另一个输入m_2,它们会生成同样的输出
  • 冲突抵抗性(Collision resistant):难以找到两个不同的输入m_1,m_2,它们具有相同的输出。这点要比Target collision resistant 更严格

应用

  • Git,用于内容地址存储(content-addressed storage)。问题在于,既然有非加密用哈希函数,为什么Git要使用加密用哈希函数呢?
  • 文件内容的简介。如对软件的安装包使用哈希函数生成哈希值,通过校验官网提供的软件哈希值与自己从第三方镜像网站下载软件的哈希值,即可知道自己下载的是否为原装正版。

密钥派生函数(KDF-Key derivation functions)

KDF一般用于为其他加密算法生成固定长度的密钥以供使用。

通常KDF会被故意设计的较慢,用以延缓线下暴力破解。

应用

  • 为其他加密算法生成密钥
  • 强化登录凭证:在存储密码时,添加一些随机生成的 进去,存储的是KDF(password + salt)。在验证登录时,使用存储的 逆运算KDF,得到密码进行验证。

对称加密(Symmetric cryptography)

当前最常用的对称加密算法是AES算法。对称加密算法实现了如下的几个函数:

keygen() -> key  (this function is randomized)

encrypt(plaintext, key) -> ciphertext  (密文)
decrypt(ciphertext, key) -> plaintext  (明文)

非对称加密(Asymmetric cryptography)

非对称加密分为公钥私钥。公钥可以发布到互联网上,不会对信息的安全造成影响;私钥需要私人管理。

非对称加密实现了如下的函数,来进行加/解密,或 签名/验证。

keygen() -> key  (this function is randomized)

encrypt(plaintext, key) -> ciphertext  (密文)
decrypt(ciphertext, key) -> plaintext  (明文)

sign(message, private key) -> signature  (签名)
verify(message, signature, public key) -> bool  (签名是否有效)

应用

  • PGP邮件加密:人们可以在PGP密钥服务器或Keybase上发布他们的公钥,这样任何人就可以向他们发送加密邮件
  • 私密信息:使用非对称加密来建立私密通信。由于非对称加密算法比较耗时,通常做法是使用对称加密算法生成密钥,通过非对称加密传输密钥,之后就可以使用加/解密较快的对称加密算法建立私密通信了。
  • 软件签名:Git有GPG签名的commit和tag,人们可以通过公布的公钥来验证下载的软件是否为原装正版。

密钥分配

在网上公布的公钥可能会被人恶意篡改,如何将网络上的公钥与现实世界中的身份对应是一个难题,以下是一些软件的解决方案:

  • 第一个人可信,第一个人相信的人也就因此可信。信任链可以传播。
  • 可信的网站
  • 公证机构

案例学习

密码管理器

使用密码管理器能避免密码的重复使用,密码管理器会使用KDF自动生成高熵密钥并进行加密存储,用户只需要记住一个高熵的密码即可。

双重验证

双重验证要求用户提供密码以及验证器(如银行的U盾)来进行验证,可以有效预防密码被偷以及钓鱼攻击

磁盘加密

可以在笔记本被偷时保护数据。

SSH

  • 使用ssh-keygen命令会生成公钥和私钥。
  • 公钥可以原样存储,而私钥则应该加密存储,因此ssh-keygen会提示用户输入passphrase
  • 当服务器拥有客户端的公钥(存储在.ssh/authorized_keys文件中)时,会使用如下方式验证客户端是否真的拥有私钥:
    1. 服务器向客户端发送任意数字
    2. 客户端使用私钥对这些任意数字进行签名,并返还签名给服务器
    3. 服务器使用客户端公钥验证签名。

10 Potpourri

课程讲义:Potpourri

键盘绑定

重新映射(示例)

  • Caps Lock映射为CtrlEscape ,因为CapsLock处于一个便于接触的位置,但使用却并不频繁

触发特定动作(示例)

  • 打开新终端或浏览器窗口
  • 插入特定的文本,如手机号、邮箱
  • 电脑休眠

高级修改(示例)

  • 重新映射按键的频率,如连续按压5次shift,触发大写锁定
  • 重新映射敲击和持续按压,如快速敲击Caps Lock触发Escape,而持续按压则输入大写

软件资源

守护进程(Daemons)

守护进程一般在系统后台运行,无需用户启动或与用户进行交互。

守护进程一般以d结尾,例如sshd

Linux守护进程

linux 之.service文件简介_Tian_cy_的博客-CSDN博客

在Linux系统中,使用systemd来运行、设置守护进程,可以使用systemctl命令来对系统中运行的守护进程进行查看、设置:

  • status:列出当前正在运行的守护进程
  • enabel
  • disable
  • start
  • restart
  • stop

添加、配置守护进程

# /etc/systemd/system/myapp.service
[Unit]
Description=My Custom App
After=network.target

[Service]
User=foo
Group=foo
WorkingDirectory=/home/foo/projects/mydaemon
ExecStart=/usr/bin/local/python3.7 app.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

FUSE(Filesystem in User Space)

Linux系统使用VFS来统一管理不同的文件系统,如ext4、ntfs等。当用户对文件系统进行操作,如touch a时,VFS会将系统调用传递到内核,内核再根据具体的文件系统调用特定的函数进行文件的创建操作。

也就是说,文件的创建过程是在内核层面的,用户不可干涉

而FUSE则允许当发送文件系统调用时运行用户层面的代码,然后将必要的系统调用传递给内核。即对于文件系统调用,用户可以执行任意的操作,如向特定邮箱发送提醒等。

FUSE实例

  • sshfs:通过SSH连接在本地打开远程文件
  • rclone:将Dropbox、谷歌云存储等云盘挂载到本地
  • gocryptfs:加密整文件系统,文件被加密存储,但当文件系统挂载时,可以像明文一样被打开
  • kbsf:对文件系统进行分区加密,可以有私人、共享、公开文件夹

备份

误区

  • 将数据在同一个磁盘上复制一份:因为磁盘一旦损坏,所有数据都会丢失
  • 将数据在另一个磁盘上复制一份:因为磁盘可能会丢失、被偷
  • 云同步:虽然方便,但一旦云存储数据被抹除、篡改,其影响会传播,即你所存储的数据可能会受到影响

策略

  • 版本化:可以方便的查看修改历史以及从历史中恢复

  • 去重:只保存修改或新增的内存,避免重复存储

  • 安全:需要考虑需要哪些信息才可以将自己的备份删除、篡改,从而选取适当的安全策略

  • 离线:将网络应用中重要的信息,如播放列表、照片等离线存储下来,避免由于账号被禁等情况丢失数据

通用命令行标记

  • --help:显示简短的使用说明
  • 许多命令行工具提供dry run模式,即展示将要进行的操作,但并不会真的执行这些操作,因此这些操作是可逆的;此外在执行破坏性操作,如删除文件时,会提供交互式标记,来提示用户确认操作
  • --version/-V:显示版本信息,在报告bug时很好用
  • --verbose/-v:显示详细的执行信息。可以使用多个v来输出更详细的信息,如-vvvv;在进行debug调试时很有用
  • --quiet:只在有错误发生时打印信息
  • -:指代标准输入/输出,具体根据工具所需是输入还是输出决定。
  • -r:递归
  • --:停止解释命令行标记,注意前后都有空格
    • 删除名为-r的文件:rm -- -r
    • 通过ssh执行带参数的命令:ssh machine -- foo --for-foo

U盘启动盘(Live USB)

U盘启动盘中包含有一个操作系统,插入U盘启动盘,在Boot界面选择U盘,即可进入U盘启动盘中的操作系统。

U盘启动盘有多种用途,如试运行软件,学习操作系,在电脑重装系统崩溃时修复数据等。

QA

课程讲义:Q&A

推荐优先学习的工具

  • 学习如何更多的使用键盘、更少的使用鼠标。如设置快捷键、修改接口等
  • 好好学习所使用的编辑器
  • 学习如何自动化/简化工作流中的重复性任务
  • 学习版本控制系统(如GIT),并结合GitHub学习如何在现代软件项目中与他人进行合作

Pyhon Vs Bah

Shell脚本的特点是为一系列特定的命令编写简短、一次性的脚本,并不适合进行大型软件开发:

  • Bash在简单的使用场合能够获得正确的结果,但对于所有可能的输入却很难保证都能获得正确的结果。例如脚本参数中的空格可能会导致数不清的bug
  • Bash的复用性并不高,因此很难复用之前写过的脚本;并且在Bash中没有库的概念。
  • Bash对于特殊值使用如$?来代表,而在其他语言中可以使用exitCode来表示

source script.sh./script.sh的区别

相同点

  • 都会运行script.sh这个脚本文件

不同点

  • 通过source执行的命令会在当前会话中发生作用,如切换目录或定义函数等
  • 通过./script.sh执行的命令时,当前会话会创建一个子会话,这些命令在子会话中执行,当执行完毕后返回父会话时,并不会对父会话造成任何改变

apt VS pip

建议

对于这个问题并没有准确的答案,但有两条建议:

  • 不要同时使用aptpip安装同一个包,以免导致难以调试的Bug
  • 尽可能使用编程语言特定的包管理器,并且使用独立环境(如Python的virtualenv)来避免污染系统环境

应该考虑的因素

  • 常用的包通过aptpip都可以获取,但不太常见或较新的包可能只能通过特定语言的包管理器获取。

  • 当使用系统的包管理器时,库文件会被在系统范围内进行安装,因此不适用于需要使用同一个包的不同版本的开发需求;相对的,大多数编程语言支持独立的虚拟环境允许按照不同版本的包,并且不同版本之间不会产生冲突,例如python的virtualenv。

  • 根据操作系统和硬件架构的不同,某些包可能会带有需要进行编译的二进制文件,如ARM电脑或树莓PI,此时使用系统包管理器要比特定语言的包管理器要好

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值