├── 02_check_sub ├── lecture07 │ ├── questions01.md │ ├── questions02.md │ └── questions03.md ├── lecture10 │ ├── questions01.md │ ├── questions03.md │ └── questions02.md ├── lecture11 │ ├── questions01.md │ ├── questions04.md │ ├── questions02.md │ └── questions03.md ├── lecture09 │ ├── questions01.md │ ├── questions02.md │ └── questions03.md ├── lecture02 │ ├── questions02.md │ └── questions01.md ├── lecture01 │ └── questions01.md ├── lecture08 │ ├── questions01.md │ ├── questions02.md │ ├── questions03.md │ └── questions04.md ├── lecture04 │ ├── question01.md │ └── question02.md ├── lecture05 │ ├── questions01.md │ └── questions02.md └── lecture06 │ ├── questions01.md │ ├── questions02.md │ └── questions03.md ├── images └── donate.jpg ├── README.md └── 01_ai_sub └── ch_subtitles ├── lecture02_ch_ai_sub.md ├── lecture07_ch_ai_sub.md ├── lecture05_ch_ai_sub.md ├── lecture08_ch_ai_sub.md ├── lecture11_ch_ai_sub.md ├── lecture04_ch_ai_sub.md ├── lecture01_ch_ai_sub.md ├── lecture03_ch_ai_sub.md ├── lecture09_ch_ai_sub.md ├── lecture10_ch_ai_sub.md └── lecture06_ch_ai_sub.md /02_check_sub/lecture07/questions01.md: -------------------------------------------------------------------------------- 1 | 1.23字幕结束太快 2 | 401en和ch重叠了. 3 | 4 | 5 | -------------------------------------------------------------------------------- /02_check_sub/lecture10/questions01.md: -------------------------------------------------------------------------------- 1 | 38需要断句.(断句的时候吧英文的括号去掉) 2 | 85的字幕消失过早 3 | -------------------------------------------------------------------------------- /02_check_sub/lecture10/questions03.md: -------------------------------------------------------------------------------- 1 | 消失过快. 2 | 9.22 3 | 14.55 4 | 5 | 中英重合了: 6 | 33.57 -------------------------------------------------------------------------------- /02_check_sub/lecture11/questions01.md: -------------------------------------------------------------------------------- 1 | 3.12 看一下 2 | 3 | 41.55缺东西了 4 | 前面好像有一段没字幕,但是我忘记在哪里了 5 | 776? -------------------------------------------------------------------------------- /images/donate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x1B05/missing-semester-cn-subtitles/HEAD/images/donate.jpg -------------------------------------------------------------------------------- /02_check_sub/lecture09/questions01.md: -------------------------------------------------------------------------------- 1 | 176密码哈希函数中"密码"加粗一下 2 | 31.51挡住了命令. 3 | 541 761,766,791括号里的特殊说明 4 | 59.33 在?处断句 -------------------------------------------------------------------------------- /02_check_sub/lecture02/questions02.md: -------------------------------------------------------------------------------- 1 | 19.17 去掉中文的lsing(我发现字幕文本里并没有,但是视频里有) 2 | 30.31 41.59 46.01挡住了命令 3 | 43.50字幕文本和视频不匹配(视频是错误的) 4 | -------------------------------------------------------------------------------- /02_check_sub/lecture01/questions01.md: -------------------------------------------------------------------------------- 1 | 373 注释 2 | 28.56 30.53中英字幕重叠了 3 | 566 567 英文太长变两行 4 | 38.35 有一些挡住命令了 5 | 46.43 注释的格式错误 6 | 47.37 字幕消失快了 7 | -------------------------------------------------------------------------------- /02_check_sub/lecture08/questions01.md: -------------------------------------------------------------------------------- 1 | 14.30-14.40 没字幕 2 | 40,47 字幕出错 3 | 42。46有一点小错,我觉得没必要改,之前好像也有一个,忘记在哪里 4 | 43.10-43.30 没字幕 5 | 6 | 108 好像有问题 7 | 132 8 | 591 9 | 中文感觉问题不少,orz -------------------------------------------------------------------------------- /02_check_sub/lecture08/questions02.md: -------------------------------------------------------------------------------- 1 | 15.53括号里的特殊说明. 2 | 3 | 14.30 4 | Very often there's sort of an assumption about what is installed on the given system. 5 | 在很多情况下,工程师们通常会假设目标系统已经安装了一些必要的软件,库或者其他系统资源 6 | 7 | -------------------------------------------------------------------------------- /02_check_sub/lecture07/questions02.md: -------------------------------------------------------------------------------- 1 | 49把老师的碎碎念的部分单独提出来 2 | 4.52 6.03 8.00 18.34 34.50 40.58背景有点影响字幕 3 | 9.09的命令没有加粗斜体 4 | 10.04 39.29的字幕出现的晚了 5 | 175的命令 6 | 27.46 33.00 36.57 37.03 37.13 37.44 51.31的字幕消失早了,以及英文改两行 7 | -------------------------------------------------------------------------------- /02_check_sub/lecture11/questions04.md: -------------------------------------------------------------------------------- 1 | 19.28 注释 [这里应该是"cache-grind"] 2 | 38.32 注释 非自由驱动程序:非自由驱动程序是指一个软件或硬件的驱动程序,它不是由自由软件许可证授权的,也不符合开源硬件标准,可能存在一些限制和约束,如不允许修改、复制、共享或分发。 3 | 41.23 注释 [编辑器的圣战就此终结!] 4 | 48.42英文的{string}还是没有显示出来,也许需要一些转义 -------------------------------------------------------------------------------- /02_check_sub/lecture04/question01.md: -------------------------------------------------------------------------------- 1 | 16 17;38 40;133 134;249 250;637 638并一起 2 | 56 中文翻译需要注意 3 | ssh要加引号? 4 | 14.25感觉只需要一个就刑了 5 | 6 | 106有学生问问题(像先前一样处理) 7 | 122有问题(少了一句so i just wanna get rid of it) 8 | 241 242 16.27(这里有点挡住命令) 9 | 挡住命令的有不少 10 | 11 | -------------------------------------------------------------------------------- /02_check_sub/lecture05/questions01.md: -------------------------------------------------------------------------------- 1 | 1.00 的英文字幕没有出现在视频中 2 | 4.00-4.28 有些不清楚的地方可以加阴影 3 | 122 123;179 180的时间轴需要微调 4 | 212 断句 5 | 我删去了234 6 | 27.20挡住命令 7 | 436 437;493 494;37 38;39 40;191 192;339 340;431 432;538 539;569 570 8 | 有关密钥的大部分都要用阴影(有的需要上移字幕例如47.00) 9 | 524需要特殊注明 10 | 11 | -------------------------------------------------------------------------------- /02_check_sub/lecture05/questions02.md: -------------------------------------------------------------------------------- 1 | 12.51-13.00命令也上移 2 | 18.00 34.57消失快了 3 | 566加个注释 [接下来这部分如果看不太懂,可以先去看lecture9] 4 | 42.31加个特殊说明 "老师这里忘记切屏幕了" 5 | 8.34 对 注销 注释,终止一个用户会话并关闭与计算机系统的连接 6 | 589的url和590的ip地址一句要连在一起 7 | 45.36英文字幕少了一点,现在补上了,时间轴要相应延长. 8 | 51.59中文也蓝色 9 | 53.08-53.16蓝色 10 | 20.54-21.33 11 | 21.29断句 -------------------------------------------------------------------------------- /02_check_sub/lecture11/questions02.md: -------------------------------------------------------------------------------- 1 | 2 | 43 44书名斜体 3 | 394 404不同人讲的话都断开 4 | 390 713 777括号特殊注明 5 | 616 ?后面断句 6 | 701 对`a和`b进行加粗和斜体,然后去掉双引号 7 | 713 .前后断句 8 | 780中文弄两行,或者断句在that和","处 9 | 10 | 11 | 138-145 12 | 3.12 看一下 13 | 分析器讲座:抽样/采样? 14 | 15 | 41.55缺东西了 16 | 前面好像有一段没字幕,但是我忘记在哪里了 17 | 776? 18 | 19 | 600 -------------------------------------------------------------------------------- /02_check_sub/lecture11/questions03.md: -------------------------------------------------------------------------------- 1 | 2.48英文书名不需要加粗只要斜体就可以. 2 | 3 | 字幕消失太快 4 | 6.37 8.23 9.23 17.02 18.01 18.14 22.28 24.13 50.08 53.20 5 | 6 | 命令加粗斜体: 7 | 9.36 10.54 44.48 8 | 46.28加粗的是"`a"和"`b"前面都有一个反引号 9 | 10 | 特殊注明: 11 | 需要把方括号里的*拿到外面 12 | 13 | 把383和384的中文调换一下. 14 | 看看388 389? 15 | 735的字幕里面的"{string}"没显示出来. 16 | -------------------------------------------------------------------------------- /02_check_sub/lecture04/question02.md: -------------------------------------------------------------------------------- 1 | 46 47有更改,需要调整时间轴,需要对46断句.同理314 315 2 | 7.21 18.18 18.34-20.22挡住命令 3 | 9.44-39 怎么突然变回去了? 背景混淆,需要换成蓝色样式 4 | 10.40-14.04 我觉得字幕还要上移一下,需要把倒数第二条命令展示出来 5 | 14.04可以让字幕回到最后一行的上方. 6 | 15.05-15.16可以把字幕弄回正下方 7 | 22.58中英重叠了. 8 | 38.34 不必要给英文变样式. 9 | 41.25-41.57的字幕都放在下方 10 | 11 | 有很多句子需要断句. 12 | 13 | 最后一句结束的早了. -------------------------------------------------------------------------------- /02_check_sub/lecture08/questions03.md: -------------------------------------------------------------------------------- 1 | 72,73的字幕时间轴有问题. 2 | 10.29有点挡字幕 3 | 10.33的命令 4 | 5 | 15.53 有关特殊说明 6 | (1)特殊说明1和2弄反了(托管是有组织的 开放是自由下载的) 7 | (2)为什么字幕里一个[1*] 另一个是[*2],按照[1*]来. 8 | (3)每个特殊说明都占一整行. 9 | (4)字可以稍微小一点 包括字幕正文里的[1*] 以及 特殊说明的字. 10 | (5)特殊说明的格式是*[1:blabla]而不是[1*blabla] 11 | 12 | 27.50 43.11,43.22 45.17 45.51的字幕消失太快. 13 | -------------------------------------------------------------------------------- /02_check_sub/lecture07/questions03.md: -------------------------------------------------------------------------------- 1 | 3.35不需要蓝色 2 | 7.33经过更改后使用统一样式就可以了. 3 | 字幕出现晚了:2.41 34.31 4 | 字幕出现早了:15.05 31.02 5 | 8.05为什么使用小号字? 6 | 10.20英文字体大小有问题 7 | 8 | 223特殊说明: 9 | 死后调试器:死后调试器会在程序崩溃后自动启动,以便收集程序崩溃时的数据,信息和堆栈跟踪等信息.除死后调试器以外,还有实时调试器. 10 | 11 | 24.46-25.06应该用蓝色或者上移解决.(上移比较好) 12 | 34.47-35.22使用蓝色样式 13 | 14 | 18.25-21.40全蓝色(中英) 15 | 53.04断句 -------------------------------------------------------------------------------- /02_check_sub/lecture06/questions01.md: -------------------------------------------------------------------------------- 1 | 196-197 少了一句: so I'll do like plus feature. 2 | 206 断句需要注意一下 3 | 354 时间轴有问题(24.55) 4 | 31.36 40.08 43.25 43.52 47.57 49.54 52.00 54.03 61.10 69.19 74.37 75.58 76.42挡住了命令 5 | 32.17 字幕分开 6 | 475 476;537 538;615 616 617;619 620英文并一起 7 | 527 528 断句 8 | 555 可删掉 9 | 1272 Pro Git使用斜体 10 | 11 | 232 42.50不懂捏 12 | 13 | 14 | -------------------------------------------------------------------------------- /02_check_sub/lecture06/questions02.md: -------------------------------------------------------------------------------- 1 | 断句: 2 | 2.46 3 | 3.17 4 | 4.04 5 | 4.18 6 | 5.02 7 | 5.05 8 | 5.37 9 | 6.14 10 | ... 11 | 字幕时间轴: 12 | 8.00 8.43 9.57 21.00 45.34 48.08 52.44消失太快 13 | 14 | 13.00英文字幕长的放第一行 15 | 16 | 每次老师问有什么问题?和学生提问后 老师的yeah.blabla分开.例如13.15 13.56 16.06 19.39 20.00 17 | 40.10 1.09.54字幕还要稍微再往上移动一些. 18 | 42.30字幕有问题 19 | 20 | 619 -------------------------------------------------------------------------------- /02_check_sub/lecture10/questions02.md: -------------------------------------------------------------------------------- 1 | 字幕消失过快: 2 | 0.15 3 | 6.10 4 | 6.38 5 | 15.47 6 | 16.31 7 | 16.43 8 | 20.37 9 | 23.21 10 | 24.58 11 | 25.43 12 | 31.59 13 | 33.56 14 | 39.54 15 | 47,52 16 | 48.34 17 | 50.02(稍快) 18 | 50.18 19 | 54.30 20 | 字幕出现稍晚: 21 | 3.47 22 | 13.24 23 | 16.43 24 | 命令需要加粗斜体: 25 | 174 26 | 187 27 | 196 28 | 784 29 | 786 30 | 789 -------------------------------------------------------------------------------- /02_check_sub/lecture09/questions02.md: -------------------------------------------------------------------------------- 1 | 10.17的"加密"的效果去掉. 2 | 3 | 31.59的字幕稍微往上移一丢丢,不要和命令挤一块儿 4 | 5 | 括号特殊说明: 6 | 97 7 | 37.14 8 | 51.19 9 | 791 10 | 11 | 字幕消失过快: 12 | 1.29 13 | 3.41(可以等老师写完再结束字幕) 14 | 26.34 15 | 30.00 16 | 36.26 17 | 37.37 18 | 37.48 19 | 38.41 20 | 40.18 21 | 41.26 22 | 42.08 23 | 42.20 24 | 754 755 字幕太快,你看着调整 25 | 53.52 26 | 55.38 27 | 58.09 28 | 58.32 29 | -------------------------------------------------------------------------------- /02_check_sub/lecture09/questions03.md: -------------------------------------------------------------------------------- 1 | 做了更改,要动时间轴: 2 | 4.53 3 | 843 844 4 | 5 | 0.36 中英行间距有问题 6 | 6.10字幕消失早了 7 | 35.09的字幕出现过慢.(这里让"Yes?" "Exactly..."分开) 8 | 9 | 15.34 注释 后门:后门是一种恶意程序,通常被恶意攻击者嵌入到软件或系统中,以便于随时掌控被攻击对象。这些程序可以将攻击者拖入受害者的计算机系统,使他们在未经授权的情况下访问被攻击对象,并执行各种危害性行为,比如窃取敏感信息、破坏数据等。 10 | 11 | 19.08少一句: 12 | Do you wanna play again? 13 | 还想再来一局吗? 14 | 15 | 48.50挡住命令 16 | 48.55挡住命令 17 | 18 | 老师问 有什么问题吗? 和 回答开头的Yeah.需要断开,即老师的提问和回答断开. -------------------------------------------------------------------------------- /02_check_sub/lecture06/questions03.md: -------------------------------------------------------------------------------- 1 | 21.40字幕有问题. 2 | 26.13 31.25 31.41字幕中英文重叠 3 | 336 337有修改,时间轴需要略作调整 4 | 5 | 断句 6 | 2.10 3.20 4.40 6.20 6.30 6.43 11.20 12.33 19.43 24.18 24.30 25.18 27.35 27.45 28.25 37.15 59.45 60.00 60.05 72.20 75.05 75.27 7 | 8 | 挡住命令 9 | 1句: 10 | 43.51 47.55 48.13 49.53 50.10 78.31 11 | 一段: 12 | 29.44-29.53 13 | 53.53-54.02 14 | 56.18-56.27 15 | 69.14-69.27 16 | 74.31-74.41 17 | 75.18-75.45 18 | 76.26-77.00 -------------------------------------------------------------------------------- /02_check_sub/lecture02/questions01.md: -------------------------------------------------------------------------------- 1 | kind of/like 有一些没有加上去. 2 | ## 需要特殊说明的地方 3 | 17.40 pid:PID代表进程标识符(Process Identifier)。每个运行中的进程都有一个唯一的PID,用于在操作系统中标识该进程。 4 | 36.16 cron:Cron是一种在Unix、Linux和类Unix操作系统中用于定期运行任务的工具。它允许用户指定一个或多个命令或脚本,并设置在指定时间或周期运行这些命令或脚本的方式。 5 | 10.42 命令替换:命令替换是一种在Unix、Linux和类Unix操作系统中常见的技术,用于在一个命令的输出中嵌套另一个命令。 6 | 13.00 14.52括号内容特殊说明 7 | 8 | ## 一些问题 9 | 18.13字幕有问题,是空白. 10 | 19.17的英文字幕没了.(and we get,lsing first "project1" and then "project2") 11 | 20.04 20.48的字幕最后命令的部分消失了(视频上没有,文本上有) 12 | 38.34 42.05 46.39挡住命令了 13 | 43.48的字幕消失快了.话还没讲完. 14 | 46.10左右的视频 中文不是那么清晰 15 | 16 | 83 84;163 162;210 211;323 324;556 557合在一起 17 | 18 | 234 235的字幕 235不要了 取234 19 | -------------------------------------------------------------------------------- /02_check_sub/lecture08/questions04.md: -------------------------------------------------------------------------------- 1 | 需要改动时间轴: 2 | 36 37 3 | 155 156 4 | 5 | 10.50加注释 [这里老师实际上使用的是"exa","exa"是更现代的"ls"] 6 | 7 | 10.22-10.28放下去 8 | 挡住命令: 9 | 9.50-10.05 挡住命令(上移) 10 | 11.37 11 | 使用蓝色样式: 12 | 10.22-10.46 13 | 11.44-11.52 14 | 15 | 23.10消失早了 16 | 17 | 少了三句(无视序号,maybe就是因为序号的原因它们没加上去.): 18 | 186 19 | 00:11:52,750 --> 00:11:56,600 20 | 让我再打开"paper.pdf",发现"Hello"出现在了paper里. 21 | And if we "xdg-open" the paper, and now there's "Hello" over there. 22 | 23 | 187 24 | 00:11:56",600 --> 00:12:05,319 25 | 不过,如果我改一下".dat"文件,然后把这个点改为8. 26 | On the other hand, if I were to change say the ".dat" file and make this point to 8. And I'll make. 27 | 28 | 188 29 | 00:12:05,319 --> 00:12:525 30 | 然后再"make",那么现在它会重新绘制图形,因为数据变了, 31 | Then now it plots again because the data changed, 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Missing-Semester 字幕工作 2 | 3 | 本文档描述了 Missing-Semester 字幕校对的工作流程和相关规范, 希望对以后的搬运工作有所帮助。为了保证字幕校对的高效性和一致性,我们采用网盘管理,而不使用 Git。同时,我制定了一套规范的命名系统,并使用 ARCTIME 软件来添加字幕。 4 | 5 | > 为啥不用 git?首先, 我们的 subtitler 对于 git 的使用不熟练; 其次反复校对的视频文件很大, 使用 git 并不方便. 现在反思, 视频文件大就不传视频文件. 6 | 7 | ## 最终版字幕制作流程 8 | 9 | 我工作制定了许多套流程, 为了尽可能提高效率, 经过逐步的优化, 最终版如下.(lecture6 之后按照此流程制造) 10 | 11 | ### 文件夹结构 12 | 13 | ``` 14 | missing-semester->init_subtitles 15 | ->en_subtitles 16 | ->lecture01_en_ai_sub.srt 17 | ->lecture02_en_ai_sub.srt 18 | .... 19 | ->ch_subtitles 20 | ->lecture01_ch_ai_sub.srt 21 | ->lecture02_ch_ai_sub.srt 22 | .... 23 | ->校对 24 | ->lecture01 25 | ->lecture01_sub_modi00.srt 26 | ->lecture01_sub_modi01.md 27 | ->lecture01_sub_modi02.srt 28 | ->lecture01_sub01.mp4 29 | ->lecture01_sub02.mp4 30 | ->questions01.md 31 | ->questions02.md 32 | ... 33 | ->lecture02 34 | ... 35 | ->成品 36 | ->lecture01 37 | ->完整名字.mp4 38 | ->完整名字.srt 39 | ->lecture2 40 | ... 41 | ``` 42 | 43 | ### 流程 44 | 45 | ```lua 46 | function subtitle_pre_processing(en_ai_subtitles): 47 | 断句 en_ai_subtitles | chatgpt | 文字处理(比如中文符号改为英文之类的) 48 | output: 断好句版 en_ai_subtitles 49 | 翻译 en_ai_subtitles | chatgpt | 文字处理 50 | output: 断好句版 ch_ai_subtitles 51 | # 实际上,chatgpt的翻译质量并不好,也许是我的prompt的问题,但我已经尽力完善prompt了 52 | 53 | function first: 54 | subtitler将ai英文,ai中文字幕添加到视频里: 55 | 大致处理好视频中的字幕断句和时间轴. 56 | -- 为了提高效率, 这里不要压制成片 57 | output: lecturex_sub_modi00.srt, [questions]上传网盘 58 | translator从网盘下载lecturex_sub_modi00.srt, [questions]: 59 | 直接看字幕文件翻译中文字幕(这里的翻译较为书面化) 60 | output:lecturex_sub_modi01.srt, [questions] 61 | function second: 62 | subtitler将lecturex_sub_modi.srt添加到视频里: 63 | 根据questions里面修改建议进行修改,压制成片 64 | output: lecturex_sub_modi02.srt, lecturex_sub02.mp4, [questions]上传网盘 65 | translator从网盘下载lecturex_sub_modi00.srt, [questions]: 66 | 校对英文字幕(这一遍尽量保证英文的准确无误) 67 | 校对中文字幕(根据视频内容, 将较为书面化的翻译口语化) 68 | 在校对过程中将 代码高亮,字幕遮挡,字幕旁批,时间轴错误,断句问题以及对应的解决方案 记录到questions 69 | output:lecturex_sub_modi03.srt, [ questions ] 70 | function after_second: 71 | subtitler将lecturex_sub_modi03.srt添加到视频里: 72 | 根据questions里面修改建议进行修改,压制成片 73 | output: lecturex_sub_modi02.srt, lecturex_sub02.mp4, [questions]上传网盘 74 | translator从网盘下载lecturex_sub_modi00.srt, [questions]: 75 | 校对中英字幕 76 | 在校对过程中将 代码高亮,字幕遮挡,字幕旁批,时间轴错误,断句问题以及对应的解决方案 记录到questions 77 | output:lecturex_sub_modi04.srt, [ questions ] 78 | function substantial_modi: 79 | first 80 | second 81 | while(not generally satisfied): after_second 82 | 83 | 84 | function minor_modi(lecturex_sub_modi[x].srt,questions): 85 | subtitler根据questions来调整字幕, 如有疑问, 在线提出协商解决. 86 | output: lecturex_sub_modi0[x+1].srt,lecturex_modi0[x+1].srt,[ questions ] 87 | translator从网盘下载lecturex_sub_modi02.srt,lecturex_modi0[x+1].srt, [questions]: 88 | 观看视频, 直接在线交流提出修改意见 89 | 压制成片 90 | 91 | 92 | main: 93 | subtitle_pre_processing 94 | substantial_modi 95 | while(not satisfied){ 96 | minor_modi 97 | } 98 | ``` 99 | 100 | ## 字幕字体样式 101 | 102 | - 普通中文: default_cn,幼圆,45,&H00FFFFFF,&H00FFFFFF,&H19000000,&H910E0807,-1,0,0,0,100.0,100.0,2.8788927,0.0,1,3.3076925,1.2,2,135,135,72,1 103 | - 普通英文: default_en,Arial,36,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100.0,100.0,0.0,0.0,1,2.0,2.0,2,10,10,30,1 104 | - 换行英文: default_en2,Arial,32,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100.0,100.0,0.0,0.0,1,2.0,2.0,2,10,10,10,1 105 | - 中文中代码标识: code,Arial,45,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,-1,0,0,100.0,100.0,0.0,0.0,1,2.0,2.0,2,10,10,40,1 106 | - 中文字幕中注释: comment1,楷体,45,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,-1,0,0,100.0,100.0,0.0,0.0,1,2.0,2.0,2,10,10,40,1 107 | - 注释本身: comment2,楷体,40,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100.0,100.0,0.0,0.0,1,1.5,2.0,2,10,10,135,1 108 | - 上移中文: move_cn,幼圆,45,&H00FFFFFF,&H000000FF,&H00000000,&H910E0807,-1,0,0,0,100.0,100.0,0.0,0.0,1,3.3,1.2,2,10,10,145,1 109 | - 上移英文: move_en,Arial,32,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100.0,100.0,0.0,0.0,1,2.0,2.0,2,10,10,110,1 110 | - 看不清的中文(加蓝边框): unclear_cn,微软雅黑,60,&H00FFFFFF,&H000000FF,&H00FF0004,&H910E0807,-1,0,0,0,100.0,100.0,0.0,0.0,1,1.0,1.2,2,10,10,145,1 111 | - 看不清的英文(加蓝边框): unclear_en,Arial,32,&H00FFFFFF,&H000000FF,&H00FF0004,&H00000000,-1,0,0,0,100.0,100.0,0.0,0.0,1,1.0,2.0,2,10,10,110,1 112 | 113 | ## 涉及到的问题以及对应的解决方案: 114 | 115 | - 断句: 116 | - 一般在标点处, and 处, 不同人说话的地方断句. 117 | - 注释样式: 118 | - 英文字幕不改动.中文字幕采用comment1样式. 119 | - 中文字幕中需要注释的名词在后面加`[1*]` 120 | - 注释本身`*[1:]` 121 | > 如果一行字幕出现多个注释样本, 则数字相应增加. 122 | - 英文有时候会比中文长很多 123 | - 对英文换行处理 124 | - 遮挡: 125 | - 如果字幕遮挡到命令, 首先选择上移字幕, 如果上移后背景纷杂影响到字幕,则对上移后的字体采用unclear样式(加粗, 换颜色, 加边框..). 126 | - 如果未遮挡命令,而背景对字幕有扰乱效果,则直接原地unclear样式(加粗, 换颜色, 加边框..). 127 | 128 | 129 | ## 学到的一些 130 | 131 | 对于小的需求比较固定的项目来说,工作前需要精致的流程设计,尽可能地提高效率.(这里的字幕工作应该属于此类) 132 | 对于大的项目,需求比较多的项目来说,小步快跑,逐步优化也许更合适一些.(即所谓的 KISS 原则) 133 | 134 | 前期的懒惰不仔细,流程的不规范,标准的不统一,都会给后期的工作带来巨大的麻烦, 造成时间的浪费. 135 | 136 | ## 其他资源 137 | 138 | - 官方 github 仓库:https://github.com/missing-semester/missing-semester 139 | - 2020 讲义:https://missing.csail.mit.edu/ 140 | - 2020 中文讲义:https://missing-semester-cn.github.io/ 141 | - 2019 讲义:https://missing.csail.mit.edu/2019/ 142 | 143 | ## 捐赠 144 | 145 | 如果您觉得这个工作对您有帮助, 如果可以的话, 奖励我一杯冰阔落吧!感谢您的慷慨与善意. 146 | donate 147 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture02_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 今天我们将讨论与 shell 相关的两个主要主题。 2 | 首先,我们将进行一些 shell 脚本编写,主要涉及到 bash,这是大多数人在 Mac 或像大多数 Linux 系统中启动的默认 shell。 3 | 它也与其他 shell(如 zsh)兼容,非常实用。 4 | 然后,我们将介绍一些其他非常方便的 shell 工具,以便您避免执行一些重复的任务,例如查找某些代码或某些难以找到的文件。 5 | 已经有一些内置命令非常有用,可以帮助您完成这些任务。 6 | 昨天,我们已经介绍了 shell 的一些特性,以及如何开始执行命令并将其重定向。 7 | 今天,我们将更详细地介绍 shell 的变量语法、控制流和函数等内容。 8 | 例如,一旦您进入 shell,您可能想要定义一个变量,这是学习编程语言的第一步。 9 | 在这里,您可以执行类似 "foo=bar" 的操作。 10 | 现在,我们可以通过 "$foo" 来访问 foo 的值。 11 | 这是非常完美的。 12 | 需要注意的一个问题是,在处理 bash 时,空格非常关键。 13 | 主要是因为空格是保留字符,用于分隔参数。 14 | 因此,例如,"foo=bar" 将不起作用,shell 会告诉您为什么不起作用。 15 | 这是因为 shell 会将 "foo" 和 "=bar" 视为两个参数,而不是将 "foo=bar" 视为一个整体。 16 | 通常情况下,当您遇到一些问题时,例如一些带有空格的文件时,需要小心处理。 17 | 您需要小心引用字符串。 18 | 因此,我们将讨论在 bash 中如何使用字符串。 19 | 有两种方法可以定义字符串:您可以使用双引号定义字符串,也可以使用单引号定义字符串。 20 | 然而,对于文字字符串,它们是等效的,但对于其他情况则不然。 21 | 例如,如果我们执行 "value is $foo",则会将 "$foo" 展开为字符串,并将其替换为 shell 中 foo 变量的值。 22 | 而如果我们使用单引号,则只是获取 "$foo",而单引号不会进行替换。 23 | 再次强调,很容易编写一个脚本,假设这类似于您更熟悉的 Python,而没有意识到这一点。 24 | 这是您将赋值变量的方式。 25 | 然后,bash 还具有控制流技术,例如 for 循环、while 循环,还有一个主要的事情是您可以定义函数。 26 | 我们可以访问此处定义的 MCD 函数。 27 | 目前为止,我们只是演示了如何通过将多个命令使用管道传输来执行这些命令。 28 | 昨天简要介绍了这种方法。 29 | 但是在很多情况下,你会想先执行一件事,然后再做另一件事。 30 | 这就像我们在这里看到的按顺序执行的例子一样。 31 | 32 | 例如,在这里,我们调用了MCD函数。 33 | 我们首先调用makedir命令来创建这个目录。 34 | 在这里,$1是一种特殊变量。 35 | 这是Bash的工作方式。 36 | 在其他脚本语言中,可能会有类似于argv的数组,其中第一项将包含该参数。 37 | 在Bash中,它表示为$1。 38 | 通常,在Bash中,许多内容都以$加上一些内容为前缀,表示它们是保留关键字。 39 | 稍后我们会看到更多例子。 40 | 一旦我们创建了文件夹,我们就进入该文件夹,这是一种相当常见的模式。 41 | 我们可以在shell中直接输入这个命令,它会起作用并定义该函数。 42 | 但是,有时将这些内容写入文件会更好。 43 | 我们可以使用source命令来执行该脚本并在我们的shell中加载它。 44 | 现在看起来好像什么都没有发生,但是现在MCD函数已经在我们的shell中定义了。 45 | 因此,我们现在可以执行MCD test命令,然后我们从tools目录移动到test目录。 46 | 我们既创建了文件夹,又进入了它。 47 | 还有什么?我们可以使用$1来访问第一个参数。 48 | 还有很多保留命令,例如$0表示脚本的名称,$2到$9将是Bash脚本接收的第二到第九个参数。 49 | 其中一些保留关键字可以直接在shell中使用,例如$?可以获取上一个命令的错误代码,我将简要解释一下。 50 | 但例如$ _可以获取上一个命令的最后一个参数。 51 | 因此,我们也可以这样做,例如使用“mkdir test”,而不需要重复输入test,我们可以使用$_作为上一个命令的一部分来访问上一个参数,这样它将被替换为test,然后我们进入test。 52 | 还有很多这样的命令,你应该熟悉它们。 53 | 我经常使用的另一个命令叫做"bang bang"("!!"),当你尝试创建某些东西但没有足够的权限时,你会遇到它。 54 | 然后,你可以执行“sudo !!”命令,然后它将替换其中的命令,现在你可以尝试执行该命令。 55 | 现在它会提示你输入密码,因为你拥有sudo权限。 56 | 之前,我提到了错误命令。 57 | 昨天我们提到,进程可以通过不同的方式与其他进程或命令进行通信。 58 | 除了标准输入和标准输出之外,还有一个标准错误流,它用于在程序编写时输出错误信息,以避免污染标准输出。 59 | 还有一个错误代码,这是许多编程语言中通用的东西,可以报告程序的整体运行情况。 60 | 因此,如果我们执行像"echo hello"这样的命令,并且检查其错误代码,它将为零。 61 | 这是因为一切都很顺利,没有出现任何问题。 62 | 零的退出代码与C语言中的退出代码相同,例如0表示一切顺利,没有错误。 63 | 64 | 但是,有时事情并不按照我们预期的方式进行。 65 | 例如,如果我们在MCD脚本中使用"grep foobar"命令,然后检查其错误代码,它将为1。 66 | 这是因为我们试图在MCD脚本中搜索"foobar"字符串,但是该字符串不存在。 67 | 因此,grep不会输出任何内容,但是通过返回一个1的错误代码告诉我们发生了错误。 68 | 还有一些有趣的命令,比如"true",它的错误代码始终为零,而"false"的错误代码始终为1。 69 | 通过使用这些逻辑运算符,我们可以执行某些条件语句。 70 | 例如,一种方式是使用"OR"运算符连接两个命令,如果第一个命令失败,则Bash将执行第二个命令。 71 | 如果第一个命令的错误代码不为零,Bash将尝试执行第二个命令。 72 | 同样,使用"AND"运算符连接两个命令时,只有在第一个命令没有错误的情况下才会执行第二个命令。 73 | 如果第一个命令失败,则Bash将不会执行第二个命令。 74 | 此外,无论执行什么命令,都可以使用分号将它们连接在同一行中,并将它们打印出来。 75 | 另外,我们还没有学习如何将命令的输出存储到变量中。 76 | 我们可以使用命令替换实现这一点。 77 | 例如,使用"PWP"命令获取当前工作目录并将其存储到变量"foo"中,然后可以通过检查变量"foo"来查看当前工作目录。 78 | 通常情况下,可以将命令替换放入任何字符串中。 79 | 如果使用双引号而不是单引号,则该命令将被扩展为字符串并输出结果。 80 | 另一个有趣的事情是,现在该命令正在被扩展为一个字符串,而不仅仅是输出字符串。 81 | 另一个巧妙而不那么为人所知的工具叫做进程替换,它有点类似。 82 | 它会在命令中使用特殊符号(例如“<”),并与另一个括号一起使用,执行命令并将输出存储到类似临时文件的地方,并将文件句柄提供给命令。 83 | 所以在这里,我们使用进程替换获取目录列表(通过ls命令),将其存储到临时文件中,然后对父文件夹进行相同的操作,并将两个文件合并。 84 | 这种方法非常方便,因为一些命令不接受标准输入,而是期望从文件中读取参数。 85 | 这样我们就得到了两个文件的内容合并在一起的结果。 86 | 到目前为止,我们已经学到了很多信息。 87 | 现在让我们看一个简单的示例脚本,其中包含一些这样的操作。 88 | 例如,在这里,我们有一个字符串和一个$date命令。 89 | 在UNIX中有很多这样的命令,您会逐渐熟悉其中的很多。 90 | $date命令用于打印当前日期,您可以指定不同的格式。 91 | 接下来,我们会用到以下变量:$0,它代表当前脚本的名称;$#,它表示该命令传入的参数个数;$$,它是该命令的进程 ID。 92 | 可能会有很多这样的美元符号,不太直观,因为它们没有一种容易记忆的方式,例如$#。 93 | 但是,我们会逐渐熟悉它们。 94 | 在这里,我们使用了$@,它会将所有的参数展开。 95 | 96 | 这样,我们就不必假设有三个参数,并编写 $1,$2,$3。 97 | 如果我们不知道有多少个参数,我们可以将它们全部放在 $@ 中,然后将其传递给 for 循环。 98 | for 循环会按顺序获取每个参数,并依次将它们传递。 99 | 因此,我们可以为每个参数提供代码。 100 | 接下来,在下一行,我们运行 grep 命令,它会在某个文件中查找子字符串,例如在文件中查找字符串 "foobar"。 101 | 我们在该命令中使用了变量,它们已被展开。 102 | 昨天,我们讨论了如何将程序的输出重定向到文件中,以保存它或将它连接到其他文件。 103 | 但是有时我们想要相反的效果。 104 | 例如,在这里,我们关注... 105 | 我们需要注意错误代码。 106 | 对于这个脚本,我们需要关心 grep 命令是否成功运行。 107 | 因此,我们实际上可以完全忽略输出... 就像 grep 命令的标准输出和标准错误一样。 108 | 我们要做的是将输出重定向到 /dev/null,这是 UNIX 系统中的一种特殊设备,您可以像写入文件一样写入数据,但它会被丢弃。 109 | 这里有一个 ">" 符号,我们昨天已经讨论过它用于重定向输出。 110 | 这里有一个 "2>",正如一些人可能已经猜到的那样,这是用于重定向标准错误的,因为这两个流是分开的,您需要告诉 bash 如何处理它们。 111 | 因此,在这里,我们检查文件是否包含 "foobar"。 112 | 如果文件包含 "foobar",它的退出状态码将为零。 113 | 如果没有 "foobar",它的退出状态码将为非零。 114 | 这正是我们要检查的内容。 115 | 在这个 if 语句的一部分中,我们说 “给我退出状态码”。 116 | 再次使用 $? 来代表退出状态码。 117 | 然后我们使用比较运算符 "-ne",表示 “不等于”。 118 | 在一些其他编程语言中,有时会使用 "=="、"!=" 等符号。 119 | 在bash中有一组保留的比较操作符,这主要是因为当您在shell中进行测试时,可能需要测试很多东西。 120 | 例如,在这里,我们只是检查两个整数值是否相同。 121 | 或者例如在这里,使用"-f"操作符可以让我们知道文件是否存在,这是您经常会遇到的情况。 122 | 让我们回到这个例子。 123 | 当文件没有名为"foobar"的时候会发生什么?如果存在非零错误代码,则会打印“此文件没有任何foobar,我们将添加一个”。 124 | 接着,我们使用运算符“>>”在文件的末尾追加这个“# foobar”的注释。 125 | 由于文件已经被送入脚本,我们之前不知道它,因此我们必须替换文件名的变量。 126 | 我们可以实际运行这个脚本。 127 | 128 | 我们已经在这个脚本中具有正确的权限,我们可以举出一些例子。 129 | 在此文件夹中,我们有一些文件。 130 | "mcd"是我们在开头看到的MCD函数,还有一些其他的"script"函数。 131 | 我们甚至可以将自己的脚本输入到自己中,以检查它是否包含"foobar"。 132 | 然后我们运行它,首先可以看到已成功扩展的不同变量。 133 | 我们有日期,已经被替换为当前时间。 134 | 接着我们使用三个参数运行这个程序,使用随机生成的PID。 135 | 然后它告诉我们MCD没有任何"foobar",所以我们正在添加一个新的。 136 | 这个脚本文件没有"foobar"。 137 | 让我们以 MCD 为例来说明。 138 | 它有我们所需的注释。 139 | 在执行脚本时,还有一些需要注意的事情。 140 | 这里有三个完全不同的参数,但通常你可以提供更简洁的参数。 141 | 例如,如果我们想引用所有以“.sh”结尾的脚本,我们可以使用大多数 shell 支持的“globbing”文件名扩展方式,像这样执行“ls *.sh”。 142 | 这将显示任何以“sh”结尾的具有任何字符的内容,这不足为奇,我们会得到“example.sh”和“mcd.sh”。 143 | 我们还有“project1”和“project2”,如果有一个叫“project42”,我们也可以执行“project42”,例如,如果我们只想引用名字只有一个字符,但后面没有两个字符的项目,我们可以使用问号。 144 | 因此,“?”将扩展为只有一个字符。 145 | 然后,我们首先得到“project1”,然后是“project2”。 146 | 通常情况下,globbing 可以非常强大,并且可以结合使用。 147 | 一个常见的模式是使用所谓的花括号。 148 | 假设我们有一个图像文件,我们想将它从 PNG 转换为 JPG,或者我们可以将其复制,或者... 这是一种非常常见的模式,有两个或更多类似的参数,你想将它们作为参数传递给某个命令。 149 | 你可以这样做,或者更简洁地说,你可以只是做“image.{png,jpg}”。 150 | 这将扩展为上面的行,并且你可以让 zsh 为你完成这个任务。 151 | 这确实非常强大。 152 | 例如,你可以做一些像...我们可以在一组名为 foo 的文件上执行“touch”,所有这些都会被扩展。 153 | 你甚至可以在几个级别上做到这一点,你将得到笛卡尔积的结果...如果我们在这里有一个组“{1,2}”,然后这里有“{1,2,3}”,这将对这两个组的笛卡尔积进行扩展,并将扩展为所有这些东西,我们可以快速地“touch”。 154 | 你还可以将星号 glob 与花括号 glob 结合使用。 155 | 你甚至可以使用类似于区间的东西,例如,我们可以创建“foo”和“bar”目录,然后我们可以这样做。 156 | 这将会扩展到 "fooa", "foob"... 所有这些组合,一直到 "j",然后对于 "bar" 同样如此。 157 | 我还没有真正测试过它,但是我们得到了所有我们能“触及”的组合。 158 | 现在,如果我们发现这两个目录中的某些文件不同,我们可以再次运行之前的命令来替换它们。 159 | 160 | 例如,如果我们想查看这两个文件夹之间有哪些文件不同,我们可以使用“diff”命令比较它们的输出。 161 | 虽然我们可以直接看出差异在于 X 和 Y 这两个文件,但是使用“diff”命令更加普遍适用。 162 | 此外,我们仅仅看到了 Bash 脚本,但并不意味着 Bash 是最适合所有任务的脚本语言。 163 | 事实上,在某些任务中,Bash 甚至可能会很棘手。 164 | 实际上,您可以编写脚本与许多不同的 shell 交互,而不仅仅是 Bash。 165 | 让我们看一下 Python 脚本的例子。 166 | 首先,我们有一个魔法注释,不再赘述。 167 | 然后,我们有一个“import sys”语句,它类似于 Bash 中的 $0,$1 等,用于获取参数向量并将其按相反的顺序打印出来。 168 | 魔法注释(shebang)是告诉 shell 如何运行这个程序的一行注释。 169 | 如果您总是要运行像“python script.py”这样的命令,并附带“a b c”之类的参数,那么您总是可以这样做。 170 | 但是,如果您想要在 shell 中运行该脚本,则需要让 shell 知道您希望使用 Python 作为解释器来运行该文件。 171 | 这可以在第一行中指定。 172 | 此处的第一行使用了“env”命令,该命令在几乎所有系统上都是可用的(有一些例外)。 173 | 通过使用“env python”参数,它将查找并使用“python”解释器,然后根据您的环境变量来解释该文件。 174 | 这样,该脚本将更加可移植,并且可以在多台计算机上运行。 175 | 另一个需要注意的问题是 Bash 不是现代化的脚本语言。 176 | 有时候进行调试会很棘手。 177 | 默认情况下,一些故障可能不太直观,例如我们之前遇到的“foo”命令不存在的错误。 178 | 因此,在课堂笔记中,我们提供了一个非常方便的工具,叫做"shellcheck"。 179 | 它可以为您提供警告、语法错误以及其他可能存在的引用错误或文件中错放空格等问题的提示。 180 | 例如,对于非常简单的"mcd.sh"文件,我们会得到一些错误提示,比如缺少shebang。 181 | 如果在不同的系统上运行,可能无法正确解释该文件。 182 | 此外,这个CD命令可能不会正确扩展,所以您可能需要使用类似于"cd"或者"exit"这样的命令。 183 | 回到之前解释的内容,这样做的效果就是,如果CD没有正确结束,您不能CD进入该文件夹,因为要么您没有权限,要么它不存在。 184 | 这将给出一个非零的错误命令,所以您将执行退出,这将停止脚本,而不是继续执行好像您实际上并不在该文件夹中的命令。 185 | 实际上,我还没有测试过,但我认为我们可以检查"example.sh"。 186 | 在这里,我们可能需要以不同的方式检查退出代码,因为这种方法可能不是最好的方法。 187 | 我想要最后提醒的是,在编写bash脚本或函数时,有两种情况。 188 | 一种是在隔离的环境中编写脚本,就像一个您要运行的东西;另一种是将其加载到您的shell中。 189 | 我们将在命令行环境讲座中讨论一些内容,其中包括bashrc和sshrc。 190 | 191 | 总的来说,如果您在bash脚本中更改了您所在的位置,例如您CD进入一个bash脚本并且只执行该bash脚本,它不会CD进入您现在所在的shell。 192 | 但是,如果您直接将代码加载到您的shell中,例如您加载源代码函数并执行该函数,那么您将获得这些副作用。 193 | 同样的道理也适用于在shell中定义变量。 194 | 现在我将讨论一些我认为在处理shell时非常有用的工具。 195 | 这也是昨天简单介绍过的内容。 196 | 你如何知道应该使用哪些选项或确切的命令? 197 | 比如,如果我想知道LS加上"减号L"会以列表格式列出文件,或者如果我执行"move -i",它会提示我输入内容,那么您可以使用"man"命令。 198 | "man"命令提供很多信息。 199 | 例如,在这里它会解释"-i"选项有哪些可选项。 200 | 这个技巧非常有用,不仅适用于操作系统中打包好的简单命令,而且还适用于从互联网上安装的工具。 201 | 比如,如果你安装了“man”包,那么我们稍后要介绍的名为“ripgrep”的工具,其命令称为 RG,就不会随着操作系统提供。 202 | 但是它会安装自己的 man 页面,我们可以从这里访问它。 203 | 对于某些命令,man 页面非常有用,但有时候难以理解,因为它更像一份文档,介绍了工具所能做的所有事情。 204 | 有时候它包含示例,但有时候不包含,有时候工具可以做很多事情。 205 | 因此,我常常使用一些很好的工具,比如“convert”或“ffmpeg”,它们分别处理图像和视频,但它们的 man 页面很大。 206 | 因此,有一个很好的工具叫做“tldr”,你可以安装它,然后得到一些很好的示例,说明如何使用该命令。 207 | 你可以随时在谷歌上搜索,但我发现我总是要先进入浏览器,查找一些示例,然后再回来。 208 | 而“tldr”是由社区贡献的,非常有用。 209 | 此外,“ffmpeg”页面提供了很多有用的示例,这些示例的格式更好(如果你没有大号字体记录的话)。 210 | 而像“tar”这样的简单命令,则有很多选项需要组合。 211 | 比如,在这里,你可以组合 2、3 个不同的标志。 212 | 这可能不是很明显,但当你想要组合不同的标志时,这就是了解更多有关这些工具的方法。 213 | 在查找文件方面,让我们尝试学习如何查找文件。 214 | 你可以使用“ls”,比如“ls project1”,并不断使用 LS 直到底。 215 | 但是,也许如果我们已经知道我们要查找所有名为“src”的文件夹,那么可能有更好的命令来完成这个任务。 216 | 这就是“find”。 217 | Find 几乎是每个 UNIX 系统都带有的工具。 218 | 我们要在当前文件夹中调用 find,记住“.”表示当前文件夹,我们要让它的名称是“src”,类型为目录。 219 | 通过输入这个命令,它将递归地浏览当前目录,并查找所有符合此模式的文件或文件夹。 220 | Find 有很多有用的标志。 221 | 222 | 举个例子,我们想要测试路径,找出一个名为“test”的文件夹中所有的Python脚本,以及所有扩展名为“.py”的文件。 223 | 我们并不关心有多少个文件夹,只关心这些文件的存在。 224 | 同时,我们也要确保这些文件都是F类型的,即文件类型。 225 | 我们可以使用不同的标志,例如“-mtime”用于修改时间,来检查已经修改过的文件,以便只找出过去一天内修改过的文件。 226 | 这样做几乎会找到所有的文件。 227 | 通过这种方法,我们可以打印出已经创建的和已经存在的文件。 228 | find命令不仅可以查找文件,还可以在查找到这些文件时执行其他任务。 229 | 例如,我们可以使用find命令查找所有扩展名为TMP的文件,并告诉它在每个文件上运行“rm”命令。 230 | 这将删除所有这些文件。 231 | Shell的另一个好处是它提供了很多工具。 232 | 人们会不断发现新的方法,也会有替代的方法来编写这些工具。 233 | 了解这些工具非常有用。 234 | 例如,如果您只想匹配以“tmp”结尾的文件名,可能需要使用很长的命令。 235 | 但是,有一些更短的命令,例如“fd”,默认使用正则表达式并忽略git文件,因此您不必搜索它们。 236 | 此外,这些命令还支持彩色编码和更好的Unicode支持。 237 | 但是请记住,如果您知道这些工具的存在,您可以节省大量时间,不必做一些繁琐重复的任务。 238 | 类似于“find”命令的还有“locate”命令,它可以通过构建一个索引来提高查找速度。 239 | 在大多数Unix系统中,locate命令将搜索文件系统中包含指定子字符串的路径。 240 | 我实际上不确定这是否有效……好吧,它有效了。 241 | 让我试试类似于“missing-semester”这样的东西。 242 | 实际上,大多数Unix系统已经使用“locate”命令完成了这个功能,它会使用已经构建好的索引来查找包含您想要的子字符串的路径,这样可以快速完成搜索。 243 | 为了保持索引更新,我们使用正在通过cron运行的“updatedb”命令更新它。 244 | 再次查找文件非常有用。 245 | 有时候您实际上关心的不是文件本身,而是文件的内容。 246 | 您可以使用我们迄今为止介绍的grep命令来搜索文件内容。 247 | 例如,您可以在MCD中运行grep foobar来搜索文件内容。 248 | 如果您想再次递归地搜索当前结构并查找更多文件,怎么办?我们不想手动完成这项工作。 249 | 实际上,“grep”有一个“-R”标志,它将遍历整个目录。 250 | 例如,如果我们想查找所有包含“foobar”字符串的文件,我们可以使用“grep -R foobar .”命令,这会搜索当前目录和所有子目录中的所有文件。 251 | 它告诉我们在这三个位置的example.sh和在这另外两个位置的foobar中都有foobar行。 252 | 这可能非常方便。 253 | 254 | 主要用例是,您知道您已经在某种编程语言中编写了一些代码,并且知道它在您的文件系统的某个地方,但您实际上不知道在哪里。 255 | 但您可以快速搜索。 256 | 例如,我可以快速搜索我在scratch文件夹中使用请求库的所有Python文件。 257 | 如果我运行这个命令,“rg requests -t py scratch/”它会搜索所有这些文件,并精确地给出在哪一行找到了它。 258 | 这里,我没有使用grep,而是使用了“ripgrep”,它与grep的想法有点相似,但是又尝试带来更多的漂亮之处,比如彩色编码或文件处理等。 259 | 它也支持unicode。 260 | 它也非常快,因此您不必担心速度较慢的折衷。 261 | 还有许多有用的标志。 262 | 我们还可以做其他事情,例如在这里搜索。 263 | 更高级的用法,我们可以使用“-u”表示不忽略隐藏文件。 264 | 有时候你会希望忽略隐藏文件,但是如果你要搜索默认情况下隐藏的配置文件,就不应该忽略它们。 265 | 然后,我们不是打印匹配项,而是要求执行一些可能很难使用 grep 从我的头脑中想出的操作。 266 | 我想要你打印所有不匹配给出的模式的文件,这可能是一个奇怪的请求,但我们继续下去... 267 | 这里的模式是一个小正则表达式,它表示在行的开头有一个“#”和一个“!”,这是一个 shebang。 268 | 我们在这里搜索所有没有 shebang 的文件,然后在这里给它一个“-t sh”只查找“sh”文件,因为你的 Python 或文本文件可能没有 shebang 也没问题。 269 | 这告诉我们,“哦,MCD 显然缺少一个 shebang”,我们甚至可以... 270 | 它有一些不错的标志,例如如果我们包括“stats”标志,它将得到所有这些结果,但它还将告诉我们有关它所搜索的所有内容的信息,例如找到的匹配次数、行数、搜索的文件、打印的字节数等等。 271 | 与“fd”类似,有时使用一个特定的工具或另一个工具并不是很有用。 272 | 事实上,像 ripgrep 一样,还有其他几个工具。 273 | 像“ack”这样的工具是最初编写的 grep 替代工具,然后是另一个工具"ag"。 274 | 它们都可以相互替换,所以也许你所在的系统有一个而没有另一个,只要知道你可以使用这些工具就会非常有用。 275 | 最后,我想谈谈如何查找你已经找到的命令,而不是找文件或代码。 276 | 第一种显然的方法是使用向上箭头,慢慢浏览你的历史记录,寻找这些匹配项。 277 | 这实际上并不是很高效,你可能已经猜到了。 278 | 因此,Bash 有更轻松的方法来做这件事。 279 | 有一个"history"命令,会打印出你的历史记录。 280 | 这里我在 zsh 中,它只打印出一部分历史记录,但如果我说,我想要你从一切开始打印,它将从这个历史记录的开始打印一切。 281 | 由于这是很多结果,也许我们关心的是我们使用"convert"命令从某种文件类型转换为另一种文件类型的那些结果。 282 | 然后,我们获得了所有与该子字符串匹配的结果。 283 | 284 | 几乎所有的 shell 默认都会将“Ctrl+R”这个键绑定为反向搜索。 285 | 这里我们有一个反向搜索,我们可以输入“convert”,它会找到我们刚输入的命令。 286 | 如果我们一直按下“Ctrl+R”键,它会浏览这些匹配项,让我们可以在原地重新执行命令。 287 | 另一个相关的操作是使用一个非常棒的工具叫做“fzf”,它就像一个模糊查找器,可以让你进行交互式的 grep 操作。 288 | 例如,我们可以使用 cat 命令打印 example.sh 文件中的内容,并将其传递给 fzf 工具。 289 | fzf 可以获取所有的行,然后让我们可以交互式地查找我们关心的字符串。 290 | fzf 的好处是,如果你启用了默认绑定,它就会绑定到你的“Ctrl+R”键,让你能够快速地查找历史记录中转换 favicon 的时间。 291 | fzf 还具有模糊匹配的功能,而默认情况下 grep 或者其他工具必须使用正则表达式或者其他表达式来进行匹配。 292 | 在这里,我只输入了"convert"和"favicon"这两个关键字,fzf 就会尝试做出最佳的扫描,在它拥有的行中进行匹配。 293 | 最后,介绍一个你可能已经见过的工具,我用它来避免重新输入这些非常长的命令,它叫做"history substring search"。 294 | 当我在 shell 中输入命令时,它可以让我动态地在历史记录中搜索具有相同前缀的相同命令,然后,如果你……,它会在匹配列表中停止工作,并且可以使用右箭头选择要执行的命令,然后重新执行它。 295 | 我们已经看到了一些工具……我觉得还有几分钟的时间,所以我要介绍一些快速目录列表和目录导航的工具。 296 | 你可以使用"-R"选项递归地列出某个目录结构,但这可能不是最佳选择,因为不太容易理解。 297 | 有一个叫做"tree"的工具,它可以更友好地打印出所有的目录结构,并根据颜色代码进行着色。 298 | 例如,"foo" 是蓝色的,因为它是一个目录,而这个是红色的,因为它有执行权限。 299 | 但是我们还可以做得更好。 300 | 有一些很好的工具,比如最近出现的一个叫做"broot"的工具,它可以完成同样的任务。 301 | 在这里,例如不像列出每一个单独的文件,例如在"bar"中,我们有这些"a"到"j"的文件,它会提示"噢,还有更多,没有列在这里"。 302 | 实际上,我可以开始输入,它会再次匹配到那些存在的文件,我可以快速选择它们并浏览它们。 303 | 因此,了解这些事情很重要,这样你就不会浪费大量时间去寻找这些文件。 304 | 另外,我认为我也安装了一些更类似于你所期望的操作系统拥有的工具,比如Nautilus或者其中一个Mac Finder。 305 | 它们有一个交互式的输入,你可以使用导航箭头快速浏览。 306 | 这可能有点过度,但你会惊讶地发现,通过浏览某个目录结构,你可以很快地理解它。 307 | 几乎所有这些工具都会让你编辑、复制文件……如果你只是寻找它们的选项的话。 308 | 最后有一个复杂的补充内容。 309 | 我们有"cd",它很好用,可以让你到达很多地方。 310 | 但是如果你可以快速到达一些你最近或经常去的地方,那就非常方便了。 311 | 你可以用很多种方式做到这一点。 312 | 也许你会想到做书签、在shell中创建别名、创建符号链接。 313 | 但是此时,程序员已经构建了所有这些工具,所以程序员已经找到了一个非常好的方法来实现这一点。 314 | 315 | 其中一种方法是使用所谓的"auto jump"。 316 | 我认为这里没有加载……好的,别担心。 317 | 我将在命令行环境中介绍它。 318 | 我想这是因为我禁用了"Ctrl+R",这也影响了脚本的其他部分。 319 | 如果你有任何相关的问题,我很乐意回答,如果有任何不清楚的地方。 320 | 否则,我们已经编写了一堆练习,涉及这些主题。 321 | 我们鼓励你尝试它们,并在办公时间前来。 322 | 我们可以帮助你弄清如何做这些事情,或者介绍一些不明确的bash技巧. -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture07_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 欢迎回来。 2 | 今天我们将涵盖调试和分析。 3 | 在我开始之前,我们将再次提醒您填写调查问卷。 4 | 我们想从您那里得到的主要问题之一是:在最后一天,我们会回答来自您们的问题,包括我们没有涉及到的问题或者您希望我们深入讨论的问题。 5 | 我们获得的问题越多,我们就可以让这一部分更加有趣,请您填写调查问卷。 6 | 今天的讲座将涵盖很多主题。 7 | 所有主题都围绕着一个概念展开:当您的程序存在错误时,您应该怎么做。 8 | 在编程时,您大部分时间都在考虑如何实现某些功能,但是修复程序中的所有问题需要花费很长时间。 9 | 即使您的程序按照您的预期运行,它可能也会非常缓慢,或者占用很多资源。 10 | 今天我们将看到许多不同的解决这些问题的方法。 11 | 首先,第一部分是关于调试的。 12 | 调试可以有许多不同的方法,有各种各样的方式...几乎所有计算机科学学生都将使用的最简单的方法就是:您有一些代码,它的行为不符合您的预期,所以您通过添加打印语句来探索代码。 13 | 这被称为“printf调试”,它运行得非常好。 14 | 老实说,我经常使用它,因为设置简单,反馈快。 15 | printf调试的一个问题是您可能会获得大量输出,而您可能并不希望获得这么多输出。 16 | 人们已经考虑了稍微复杂一些的printf调试方法之一,即通常称为“日志记录”。 17 | 与printf调试相比,使用日志记录的优点在于,当您创建日志记录时,您并不一定是因为要修复特定的问题,而是因为您构建了一个更复杂的软件系统,并且想在某些事件发生时记录下来。 18 | 使用日志记录库的一个核心优点是您可以定义严重性级别,并且可以根据这些级别进行过滤。 19 | 让我们看一个如何实现这样一个示例。 20 | 是的,一切都符合预期。 21 | 这是一个非常愚蠢的例子:我们将随机抽取一些数字,并根据数字的值(可以解释为“错误程度”)记录数字的值,然后我们可以看看发生了什么。 22 | 我需要禁用这些格式化程序...如果我们按原样执行代码,我们只会得到输出并且不断获得更多的输出。 23 | 24 | 但是你必须盯着它看,并理解正在发生的事情。 25 | 我们不知道printf之间的相对时间,也不知道这是仅仅是一条信息消息还是有错误发生的消息。 26 | 如果我们进去,然后撤销,不是那个...就是那个,我们可以设置格式化程序。 27 | 现在输出看起来更像是这样:例如,如果你正在使用多个不同的模块进行编程,你可以使用不同的级别来标识它们。 28 | 这里,我们有调试级别、关键信息、不同级别。 29 | 这可能很方便,因为我们可能只关心错误消息。 30 | 就像那些...我们一直在编写代码,到目前为止一切顺利,突然出现了一些错误。 31 | 我们可以记录下来以确定它发生在哪里。 32 | 但是也许有很多信息消息,但我们可以通过将级别更改为错误级别来处理它们。 33 | 现在,如果我们再次运行它,我们只会在输出中得到那些错误,我们只需浏览它们以理解正在发生的事情。 34 | 当你处理日志时,另一个非常有用的工具是......随着你的观察,它变得更容易了,因为现在我们有了可以快速识别的关键和错误级别。 35 | 但由于人类是相当视觉化的生物,你可以使用终端中的颜色来识别这些内容。 36 | 所以现在,改变格式化程序,我稍微改变了输出的格式。 37 | 当我这样做时,现在每当我收到一个警告消息时,它是用黄色编码的;每当我收到像错误一样的消息时,它是模糊的红色;当它是关键的时候,我有一个加粗的红色,表示有错误发生了。 38 | 在这里,输出非常短,但是当你开始有成千上万行的日志时,这并不是不切实际的,而且在许多应用程序中每天都会发生,快速浏览并确定错误或红色区域的位置可以非常有用。 39 | 一个快速的旁注是,你可能会好奇终端是如何显示这些颜色的。 40 | 归根结底,终端只输出字符。 41 | 42 | 这个程序,还有像LS这样的其他程序,它们都有很多花哨的颜色,那么它们是如何告诉终端使用这些不同的颜色的呢?其实这些工具所做的事情并不是非常复杂,大致上是这样的。 43 | 这里有……我可以清除其余的输出,这样我们就可以专注于这个问题。 44 | 这里有一些特殊字符,一些转义字符,然后是一些文本,最后是一些其他特殊字符。 45 | 如果我们执行这行代码,我们就会得到一个红色的"This is red"。 46 | 你可能已经注意到了,我们这里有一个"255;0;0",这只是告诉终端我们想要的颜色的RGB值。 47 | 你几乎可以在你任何的代码中做到这一点,这样你就可以给输出编码颜色了。 48 | 你的终端相当花哨,并支持输出许多不同的颜色。 49 | 这还不是全部,只有十六分之一。 50 | 我认为了解这个可能非常有用。 51 | 另一个问题是,也许你不喜欢或者觉得日志不适合你。 52 | 但问题是,许多其他系统可能会使用日志。 53 | 随着你构建越来越大的系统,你可能会依赖于其他依赖项。 54 | 常见的依赖项可能是Web服务器或数据库,这是非常常见的。 55 | 它们会在自己的日志中记录它们的错误或异常。 56 | 当然,你会收到一些客户端错误,但有时候这些错误信息并不足以让你弄清楚发生了什么。 57 | 在大多数UNIX系统中,日志通常放在一个名为"/var/log"的文件夹下,如果我们列出它,我们就可以看到这里有很多日志。 58 | 所以我们有像关机监视器日志或一些周报告,例如与Wi-Fi相关的事情。 59 | 如果我们输出系统日志,其中包含了很多关于系统的信息,我们就可以得到关于正在发生的事情的信息。 60 | 同样,有一些工具可以让你更好地查看这个输出。 61 | 但是在这里,我可以查看系统日志,并说:哦,有一些服务以异常代码退出了,基于这些信息,我可以去尝试弄清楚发生了什么,发生了什么错误。 62 | 当你使用日志时需要知道的一件事是,更传统的是,每个软件都有自己的日志, 63 | 比起过去,现在越来越流行将所有日志放入统一的系统日志中。 64 | 几乎任何应用程序都可以记录进系统日志,但它们不是以普通文本格式存储,而是以某种特殊格式进行压缩。 65 | 我们在数据整理讲座中就讲到过一个例子,那就是我们使用的 "journalctl",它可以访问日志并输出所有输出。 66 | 在Mac上,现在使用的命令是 "log show",它会显示大量信息。 67 | 我只会显示最近的十秒钟,因为日志非常冗长,仅仅显示最近10秒钟仍然会输出相当大量的行。 68 | 如果我们回顾一下正在发生的事情,我们会发现有很多苹果产品正在运行,因为这是一台MacBook。 69 | 也许我们能在这里找到一些关于系统问题的错误。 70 | 再说一遍,它们相当冗长,所以你可能需要练习你的数据整理技巧,例如10秒钟相当于大约500行日志,所以你可以大概知道每秒钟有多少行。 71 | 它们不仅可以用于解析其他程序的输出,也可以用于您自己的日志记录。 72 | 因此,使用 "logger" 命令,在Linux和Mac中,您可以说,好的,我要将 "Hello Logs" 记录在此系统日志中。 73 | 我们执行该命令,然后可以通过查看最近一分钟的日志来检查,因为它会非常近,然后通过grep搜索 "Hello" 来找到我们的条目。 74 | 我们发现最近创建的条目,显示了 "Hello Logs"。 75 | 随着您越来越熟悉这些工具,您会发现自己越来越经常使用日志,因为即使您有一些错误,而且程序已经运行了一段时间,也许信息已经在日志中,可以告诉您足够的信息来找出问题所在。 76 | 然而,printf调试并不是万能的。 77 | 现在我要介绍调试器。 78 | 但首先,有关日志的任何问题吗?那么您可以从日志中了解到什么样的信息?像这个 "Hello Logs" 就表明在那个时间你执行了一些关于Hello的操作吗?是的,例如,我可以编写一个Bash脚本来检测... 每次检查我连接到哪个Wi-Fi网络。 79 | 每次它检测到网络已更改,它就会在日志中创建一个条目,并说:哦,现在看起来我们已经更换了Wi-Fi网络。 80 | 81 | 然后你可以回过头来解析日志,比如:“好的,我的电脑是何时从一个Wi-Fi网络切换到另一个网络的?”这只是一个简单的例子,但这里有许多许多类型的信息可以被记录。 82 | 更普遍的情况是,你可能想要检查你的电脑是否因为某些未知原因进入了睡眠状态,比如它处于休眠模式。 83 | 日志中可能有一些信息说明是谁要求它进入睡眠状态,或者为什么会发生这种情况。 84 | 还有其他问题吗?好的。 85 | 所以当printf调试不够用时,最好的替代方法是使用……(退出)使用调试器。 86 | 调试器是一种工具,它将包装你的代码并让你运行代码,但它会对其进行控制。 87 | 因此,它将允许你逐步执行代码并设置断点。 88 | 如果你曾经使用过类似IDE这样的工具,你可能已经以某种方式看到过调试器,因为IDE具有这种花哨的功能:在这里设置断点,执行……但是归根结底,这些工具使用的只是命令行调试器,并且它们只是以一种非常花哨的格式呈现它们。 89 | 在这里,我们有一个完全损坏的冒泡排序算法。 90 | 不用担心细节,但我们只是想要对这里的数组进行排序。 91 | 我们可以尝试通过执行Python bubble.py来做到这一点。 92 | 当我们这样做时……哦,出现了一些索引错误,列表索引超出范围。 93 | 我们可以开始添加打印输出,但如果字符串很长,我们可能会得到很多信息。 94 | 那么,我们如何回到崩溃时刻呢?我们可以回到那个时刻并检查程序的当前状态。 95 | 所以,为了做到这一点,我将使用Python调试器来运行程序。 96 | 在这里,我技术上使用的是ipython调试器,因为它具有漂亮的语法着色,所以对于我们两个来说更容易理解输出中正在发生的事情。 97 | 但它们基本上是相同的。 98 | 我们执行这个,现在我们得到一个提示,告诉我们我们在程序的第一行。 99 | 100 | 我们可以...... "L"代表"List",所以和许多这样的工具一样,有一种操作语言,它们通常是助记符,就像VIM或TMUX一样。 101 | 所以在这里,“L”是为了“列出”代码,我们可以看到整个代码。 102 | "S"代表"Step",它可以让我们逐行执行代码。 103 | 问题是,我们只有在某段时间后才会触发错误。 104 | 所以我们可以重新启动程序,而不是尝试一步步走到错误处,我们可以只要求程序继续运行,即“C”命令,然后我们就到了问题所在。 105 | 我们到达了这一行,一切都崩溃了,我们得到了这个“list index out of range”错误。 106 | 现在我们可以说,嗯?好的,首先,让我们打印数组的值。 107 | 这是当前数组的值,所以我们有六个项。 108 | 好的。 109 | 这里“J”的值是多少?所以我们查看“J”的值。 110 | “J”在这里是5,这将是最后一个元素,但“J”加1将是6,所以这触发了越界错误。 111 | 所以我们要做的是,这个“N”,不是“N”,而是“N减1”。 112 | 我们已经确定错误出现在这里。 113 | 所以我们可以退出,也就是“Q”。 114 | 再次,因为它是一个事后调试器。 115 | 我们回到代码并说好的,我们需要添加这个“N减1”。 116 | 这将防止列表索引超出范围,如果我们再次运行此代码而不使用调试器,好的,现在没有错误了。 117 | 但是这不是我们排序后的列表。 118 | 这是排序的,但不是我们的列表。 119 | 我们缺少列表中的某些条目,所以我们在这里遇到了一些行为问题。 120 | 再次,我们可以开始使用printf调试,但现在有一种预感,可能我们在冒泡排序程序中交换条目的方式是错误的。 121 | 我们可以使用调试器来解决这个问题。 122 | 我们可以逐步进行交换操作,并检查交换是如何执行的。 123 | 简单概述一下,我们有两个for循环,在最嵌套的循环中,我们检查数组是否大于另一个数组。 124 | 问题是,如果我们只试图执行到这一行,它只会在我们进行交换时触发。 125 | 126 | 所以我们可以在第六行设置一个断点。 127 | 我们可以在这一行创建一个断点,程序执行到尝试交换变量时,程序就会停止。 128 | 所以我们在那里创建一个断点,然后继续执行程序。 129 | 程序停止,并说“嘿,我已经执行并到达这一行了”。 130 | 现在我们可以使用“locals()”函数,它返回一个包含所有值的字典,以便快速查看整个上下文。 131 | 字符串和数组都没问题,而且数组有6个元素,这只是一个开端。 132 | 我继续执行下一行,哦,我找到了问题:我只交换了一个变量,而不是同时交换,这就是我们逐渐丢失变量的原因。 133 | 这只是一个非常简单的例子,但调试器非常强大。 134 | 大多数编程语言都提供一些调试器,当你进行更低级别的调试时,你可能会遇到像GDB这样的工具。 135 | 你可能想使用类似于GDB这样的工具。 136 | GDB非常适用于C/C++和所有这些类似C的语言。 137 | 但是GDB实际上可以让您使用几乎任何可执行的二进制文件。 138 | 例如,在这里,我们有一个sleep程序,它只是一个将休眠20秒的程序。 139 | 它被加载,然后我们可以运行它,并通过发送中断信号来中断它。 140 | GDB正在为我们显示程序中发生的非常低级别的信息。 141 | 因此,我们正在获取堆栈跟踪,我们可以看到我们在这个nanosleep函数中,我们可以看到机器上所有硬件寄存器的值。 142 | 因此,您可以使用这些工具获取许多低级别的详细信息。 143 | 我想这就是我想介绍调试器的所有内容了。 144 | 是否有相关的问题?调试时另一个有趣的工具是,有时您想调试的方式就像您的程序是一个黑盒子。 145 | 因此,您可能知道程序的内部,但同时,您的计算机也知道程序何时尝试执行某些操作。 146 | 因此,在UNIX系统中,有这样一个概念,即用户级代码和内核级代码。 147 | 当您尝试执行一些操作,例如读取文件或读取网络连接时,您将不得不执行称为系统调用的操作。 148 | 149 | 你可以获取一个程序并查看其操作,询问此软件执行了哪些操作?例如,如果你有一个只应执行数学运算的Python函数,并将其运行通过此程序,却发现它实际上在读取文件,这是为什么?它不应该读取文件。 150 | 那么,我们可以使用"strace"这个工具。 151 | 例如,我们可以这样做。 152 | 我们将运行"LS - L"命令,忽略LS的输出,但不忽略STRACE的输出。 153 | 如果我们执行它,我们将得到大量输出。 154 | 这是所有不同系统调用,这个LS命令已执行的调用。 155 | 你会看到很多OPEN调用,你会看到FSTAT。 156 | 例如,因为它必须列出在此文件夹中的所有文件的属性,我们可以检查LSTAT调用。 157 | 因此,LSTAT调用将检查文件的属性,我们可以看到,实际上所有在此目录中的文件和文件夹都是通过LS系统调用进行访问的。 158 | 有趣的是,有时你实际上不需要运行代码就可以找出代码中的问题。 159 | 到目前为止,我们已经看到了足够多的通过运行代码来识别问题的方式,但如果你像这个屏幕上显示的代码一样看着一段代码,你也可以找到一个问题。 160 | 例如,这里我们有一些非常愚蠢的代码。 161 | 它定义了一个函数,打印几个变量,乘以一些变量,然后等待一段时间,然后我们尝试打印BAZ。 162 | 你可以试着看这个代码,然后说,“BAZ从未在任何地方定义过。 163 | 这是一个新的变量。 164 | 你可能想说BAR但你打错了。 165 | 问题是,如果我们尝试运行此程序,它将需要60秒,因为我们必须等待time.sleep函数完成。 166 | 167 | 这里,time.sleep() 只是为了示例而已,但通常你可能会加载一个数据集,因为需要将所有数据复制到内存中而需要很长时间。 168 | 事实上,有些程序会将源代码作为输入,对其进行处理,并指出代码的哪些部分可能是错误的。 169 | 在Python或一般情况下,这些被称为静态分析工具。 170 | 在Python中,例如pyflakes。 171 | 如果我们将这段代码输入pyflakes中,pyflakes会给我们指出两个问题。 172 | 第一个问题是......第二个问题是我们已经指出的:这里有一个未定义的变量名叫做BAZ。 173 | 你可能需要对此进行处理。 174 | 另一个问题是:你正在重定义那一行中的FOO变量名。 175 | 所以,这里我们定义了一个FOO函数,然后我们通过使用一个循环变量来掩盖该函数。 176 | 现在我们定义的那个FOO函数已经不可访问了,如果我们之后尝试调用它,就会出现错误。 177 | 还有其他类型的静态分析工具。 178 | MYPY是另一个不同的工具。 179 | MYPY会报告相同的两个错误,但它也会抱怨类型检查。 180 | 它会说,这里你正在将一个整数乘以一个浮点数,如果你关心代码的类型检查,你不应该混淆它们。 181 | 这可能有点不方便,需要运行这个工具,查看出错的行,然后回到你的VIM或编辑器中,找出这个错误的位置。 182 | 已经有解决方案了。 183 | 一种方法是将大多数编辑器与这些工具集成在一起,这里...你可以看到bash上有一些红色的高亮,它会读取这里的最后一行。 184 | 未定义的变量 'baz'。 185 | 所以,当我编辑这段Python代码时,我的编辑器会给我反馈,告诉我这里有什么问题。 186 | 或者,这里有另外一个问题,说你正在重新定义未使用的foo。 187 | 甚至,有些投诉是关于代码的风格。 188 | 例如,我期望有两个空行。 189 | 190 | 在Python中,函数定义之间应该有两行空行。 191 | 有一个关于很多不同编程语言的静态分析工具的课程笔记资源。 192 | 甚至还有针对英语的静态分析工具。 193 | 我有这门课的笔记,在这个英语静态分析工具“writegood”中运行,它会抱怨一些风格上的问题。 194 | 例如,“very”是一个模糊的词汇,我不应该使用它,“quickly”也会削弱含义。 195 | 你可以用它来检查拼写错误或进行很多不同类型的风格分析。 196 | 目前有什么问题吗?哦,我忘了提到......根据你正在执行的任务,将有不同类型的调试器可用。 197 | 例如,如果你在进行Web开发,Firefox和Chrome都有非常好的用于调试网站的工具集。 198 | 所以,我们在这里进行元素检查,我们可以得到......你知道吗?如何让这个更大......我们获取了这门课网页的整个源代码。 199 | 哦,是的,这个好了吗?现在我们可以实际上更改有关课程的属性。 200 | 我们可以编辑标题。 201 | 说,这不是一个关于调试和分析性能的课程。 202 | 现在网站的代码已经改变了。 203 | 这就是为什么你永远不应该相信任何网站的截图的原因之一,因为它们可能被完全修改。 204 | 你还可以修改这个样式。 205 | 例如,这里我使用了深色模式首选项,但我们可以改变它。 206 | 因为在最后一天,浏览器是为我们渲染这个网站的。 207 | 我们可以检查cookie,但有很多不同的操作。 208 | JavaScript还有一个内置的调试器,因此你可以逐步调试JavaScript代码。 209 | 因此,总结一下,取决于你正在做什么,你可能需要搜索程序员为自己构建的工具。 210 | 211 | 现在我要转换话题,不再谈论调试,也就是找到代码中的问题,而是开始谈论如何使用性能分析。 212 | 性能分析可以帮助你优化代码,可能是因为你想优化CPU、内存、网络等方面的性能。 213 | 有许多不同的原因需要进行优化。 214 | 就像调试一样,很多人有的第一种方法是使用printf进行性能分析,例如我们可以记录当前时间,然后执行一些操作,再记录时间并从原始时间中减去它。 215 | 这样你就可以缩小范围,分析不同部分代码之间的时间差。 216 | 这很好。 217 | 但有时结果可能会有点意外。 218 | 例如,这里我们sleep了0.5秒,输出结果显示是0.5加上一些额外的时间,这很有趣。 219 | 如果我们继续运行它,我们会发现一些小的误差。 220 | 事实上,我们正在测量的是通常被称为“实际时间”的时间。 221 | 实际时间就像你拿着一只钟表,在程序开始时启动,程序结束时停止。 222 | 但问题是,在你的计算机中,不仅仅是你的程序在运行。 223 | 还有许多其他程序同时在运行,可能是那些正在占用CPU的程序。 224 | 因此,为了理解这一点,你会发现很多程序使用的术语是实际时间、用户时间和系统时间。 225 | 实际时间就是我解释的整个时间长度,从开始到结束。 226 | 然后有用户时间,这是你的程序在CPU上执行用户级别周期所花费的时间。 227 | 正如我提到的,在UNIX中,你可以运行用户级别代码或内核级别代码。 228 | 系统时间则是相反的,它是你的程序在CPU上执行内核模式指令所花费的时间。 229 | 230 | 让我们通过一个示例来展示。 231 | 这里我要使用一个名为"time"的命令,它将为以下命令获取这三个指标,然后我只是从一个托管在西班牙的网站中获取一个URL。 232 | 因此,这将需要一些额外的时间来去那里并返回。 233 | 如果我们看到,这里,在程序的开头和结尾之间有两个打印语句。 234 | 我们可能会认为这个程序需要大约600毫秒才能执行,但实际上大部分时间都花在等待网络响应上,而我们实际上只花了16毫秒的用户级别和9秒的总时间,实际上执行了CURL代码的时间只有25毫秒,其他都是在等待。 235 | 有关计时的任何问题吗?好的,所以计时可能会变得棘手,这也是一种黑匣子解决方案。 236 | 或者如果您开始添加打印语句,那么到处都是打印语句会变得很难。 237 | 所以程序员已经找到了更好的工具。 238 | 这些通常称为"分析器"。 239 | 我要快速提醒一下的是,分析器通常是指CPU分析器,因为它们是最常见的,可以确定CPU上花费时间的位置。 240 | 分析器通常有两种类型:跟踪分析器和采样分析器。 241 | 知道它们之间的区别很好,因为输出可能会不同。 242 | 跟踪分析器会对您的代码进行插桩。 243 | 所以它们会随着您的代码执行,并在每次代码进入函数调用时进行记录。 244 | 就像这个时刻我们正在进入这个函数调用,然后继续前进,一旦完成,它们可以报告你在这个函数中执行了多少时间,在另一个函数中执行了多少时间,以此类推,这就是我们现在将要看到的例子。 245 | 另一种类型的工具是跟踪采样分析器。 246 | 跟踪分析器的问题在于它们会增加很多开销。 247 | 就像您可能正在运行您的代码,并且在您旁边进行这些统计分析,这将影响您程序的性能,所以您可能会得到稍微偏差的统计数。 248 | 249 | 采样分析器会执行您的程序,并在每100毫秒、10毫秒或其他定义的时间段内停止程序。 250 | 它会停止程序,查看堆栈跟踪并确定当前在层次结构中的哪个函数正在执行。 251 | 理念是只要您足够长时间执行此操作,就可以获得足够的统计数据,了解大部分时间都花在了哪里。 252 | 接下来让我们看一个跟踪分析的例子。 253 | 这里有一段代码,它只是 Python 中 grep 的一个简单重新实现。 254 | 我们想要检查程序的瓶颈在哪里。 255 | 我们只是打开了一堆文件,试图匹配这个模式,然后在找到匹配项时打印出来。 256 | 可能是正则表达式,也可能是打印操作,我们不确定。 257 | 为了在 Python 中执行这个操作,我们使用 "cProfile"。 258 | 我调用这个模块,说我想按总时间排序,我们会很快看到这个。 259 | 我调用我们刚才在编辑器中看到的程序。 260 | 我要执行这个程序一千次,然后将 grep 的参数应用于这里的所有 Python 文件。 261 | 这将产生一些输出,然后我们将查看它。 262 | 首先是所有 grep 的输出,但最后,我们得到了分析器本身的输出。 263 | 如果我们向上滚动,我们可以看到,通过排序,我们可以看到总调用次数。 264 | 因为我们执行了这个程序一千次,所以我们执行了8000次调用,这是我们在这个函数中花费的总时间(累计时间)。 265 | 在这里,这个内置方法 IO open 表示我们花费了大量时间等待从磁盘读取或...在这里,我们可以检查,大量时间也花在了尝试匹配正则表达式上,这是可以预期的。 266 | 267 | 使用此追踪分析器的一个警告是,正如您可以看到的那样,我们看到了我们的函数,但也看到了很多与内置函数对应的函数。 268 | 因此,像来自库的第三方函数这样的函数。 269 | 随着您构建越来越复杂的代码,这将变得更加困难。 270 | 这里是另一个Python代码片段,不要阅读它,它只是获取课程网站,然后打印出所有的超链接。 271 | 因此有这两个操作:前往该网站,获取并解析它,然后打印链接。 272 | 我们可能想要了解这两个操作之间的比较。 273 | 如果我们尝试执行cProfiler,我们将执行相同的操作,这不会打印任何内容。 274 | 我正在使用到目前为止我们没有见过的一个工具,但我认为它很好。 275 | 它是"TAC",是"CAT"的反向,并且会反转输出,因此我不必上下查看。 276 | 我们这样做,嘿,我们得到了一些有趣的输出。 277 | 我们在这个内置方法socket_getaddr_info、_imp_create_dynamic和method_connect中花费了大量的时间以及posix_stat…,我的代码中没有直接调用这些函数,因此我不知道发出Web请求和解析该Web请求的输出之间的拆分是什么。 278 | 因此,我们可以使用不同类型的分析器,即行分析器。 279 | 行分析器只会以更易读的方式呈现相同的结果,即对于此代码行,这是花费的时间。 280 | 因此,我们必须向Python函数添加一个装饰器来完成这项工作。 281 | 当我们这样做时,我们现在获得了略微裁剪的输出,但主要思路是我们可以查看时间百分比,并且我们可以看到进行此请求、获取操作所花费的时间占总时间的88%,而解析响应所花费的时间仅占10.9%。 282 | 这可能非常有信息量,许多不同的编程语言都将支持这种类型的行分析。 283 | 284 | 有时你可能不关心 CPU 的性能,也许你关心内存或其他资源。 285 | 同样地,也有内存分析器:在 Python 中有 "memory_profiler",在 C 中有 "Valgrind"。 286 | 这里是一个相当简单的例子,我们只是创建了一个包含一百万个元素的列表。 287 | 这将占用大约几兆字节的空间,并且我们做同样的事情,创建另一个包含两千万个元素的列表。 288 | 为了检查内存分配情况,我们可以使用一个内存分析器来执行它,并告诉我们总内存使用量和增量。 289 | 我们可以看到一些开销,因为这是一种解释性语言,当我们创建一个包含一百万条条目的列表时,我们将需要这么多兆字节的信息。 290 | 然后我们又得到了另外 150 兆字节。 291 | 接下来,我们释放了这个条目,总量就减少了。 292 | 我们没有因为错误而得到负增量,可能是在分析器中出现了 bug。 293 | 但是,如果你知道你的程序占用了大量内存,而你不知道为什么,可能是因为你在复制对象,而不是应该在原地进行操作,那么使用内存分析器就可以非常有用。 294 | 实际上,有一个练习将带领你完成这个操作,比较就地排序的版本和不是就地排序的版本,后者会不断制作新的副本。 295 | 如果你使用内存分析器,你可以得到两者之间的很好的比较。 296 | 有没有关于分析的问题?内存分析器是在运行程序以获得结果吗?是的……你可能能够通过查看代码来弄清楚。 297 | 但是随着程序越来越复杂,这就不是那么容易了(至少对于这个代码而言)。 298 | 这个工具会运行程序,并在每一行开始时查看堆,然后说“我现在分配了哪些对象?我有七兆字节的对象”,然后再到下一行,再次查看,“噢,现在我有 50 兆字节,所以我现在增加了 43 个”。 299 | 你可以在代码中要求这些操作的每一行自己完成这个操作,但这不是你应该做的,因为别人已经为你编写了这些工具。 300 | 正如 strace 的情况一样,你可以在分析中做类似的事情。 301 | 也许你不关心具体的代码行,但是你想检查外部事件。 302 | 303 | 你可能想检查你的计算机程序使用了多少CPU周期,或者它创建了多少页面错误。 304 | 也许你的缓存位置很差,这些问题会以某种方式表现出来。 305 | 为此,有一个“perf”命令。 306 | perf命令会运行你的程序,并跟踪所有这些统计数据,并将它们反馈给你。 307 | 如果你在低层级工作,这将非常有帮助。 308 | 我们执行此命令,我将简要解释它在做什么。 309 | stress程序只是在CPU中运行,它只是一个程序,用于独占一个CPU,并测试您是否可以独占CPU。 310 | 现在,如果我们Ctrl-C,我们可以返回并获取有关页面错误数量或我们的代码使用的CPU周期的一些信息。 311 | 对于某些程序,你可以查看正在使用的函数。 312 | 因此,我们可以记录此程序正在执行的操作,我们不知道这是因为这是其他人编写的程序。 313 | 我们可以通过查看堆栈跟踪来报告它正在执行的操作,然后说,哦,它花费了大量时间在这个__random_r标准库函数上。 314 | 这主要是因为独占CPU的方法是通过创建越来越多的伪随机数。 315 | 还有一些未映射的其他函数,因为它们属于程序,但如果你知道你的程序,你可以使用更多的标志显示有关perf的信息。 316 | 有关如何使用这个工具的非常好的在线教程。 317 | 还有一个关于分析器的问题,到目前为止,我们已经看到这些分析器在聚合所有这些信息并为你提供大量这些数字方面非常出色,这样你就可以优化你的代码或者你可以理解正在发生的事情, 318 | 人类不擅长处理大量数字,而且由于人类更善于视觉处理,因此使用可视化工具更容易理解。 319 | 程序员已经想到了解决方法。 320 | 一些流行的可视化工具包括 FlameGraph。 321 | FlameGraph 是一种采样分析器,通过运行代码并采样来收集数据。 322 | 在 y 轴上,我们有堆栈的深度,因此我们知道 bash 函数调用了另一个函数,然后调用了另一个函数,以此类推。 323 | 在 x 轴上,它不是时间戳,而是执行时间。 324 | 因为这是采样分析器:我们只能得到程序运行的一些瞬间的样本。 325 | 但我们知道例如这个主程序需要最长的时间,因为 x 轴与此成比例。 326 | 它们是交互式的,并且对于确定程序中的热点非常有用。 327 | 另一种显示信息的方法是使用调用图,也有关于如何使用这种工具的练习。 328 | 调用图将显示信息,并创建一个函数调用了哪个其他函数的图。 329 | 然后,您可以获得有关信息,例如我们知道 "main" 调用了这个 "Person" 函数十次并且它花费了这么多时间。 330 | 随着程序越来越大,查看其中一个调用图可以帮助确定您的代码的哪个部分调用了非常昂贵的 IO 操作,例如。 331 | 最后,有时您甚至可能不知道程序中哪个资源受限。 332 | 例如,如何知道我的程序使用了多少 CPU 或内存。 333 | 因此,有许多非常聪明的工具可以做到这一点,其中之一是 HTOP。 334 | HTOP 是一个交互式的命令行工具,它显示此机器具有的所有 CPU,即 12 个。 335 | 它显示内存量,并显示我使用了接近 32GB 机器内存中的 1GB。 336 | 337 | 然后我得到了所有不同的进程。 338 | 例如我们有zsh、mysql和其他在这台机器上运行的进程,我可以通过它们消耗的CPU数量或它们运行的优先级进行排序。 339 | 我们可以通过运行stress命令来检查。 340 | 在这里,我们再次运行stress命令以占用四个CPU,并检查我们是否可以在HTOP中看到它们。 341 | 我们确实发现了这四个CPU任务,现在我发现除了之前我们看到的,现在我还有这个“stress -c”命令在运行并占用了我们大量的CPU。 342 | 尽管您可以使用分析器来获取类似于此的信息,但是HTOP以实时交互的方式显示此类信息可能更快速和更易于解析。 343 | 在笔记中,有一个非常长的列表,列出了用于评估系统不同部分的各种工具。 344 | 那可能是用于分析网络性能的工具,或者查看IO操作的数量,以便您知道是否饱和了从磁盘读取,您还可以查看空间使用情况。 345 | 我想在这里,NCDU…有一个名为“du”的工具,它代表“磁盘使用”,我们有“-h”标志用于“人类可读的输出”。 346 | 我们可以进行视频,然后获得有关此文件夹中所有文件大小的输出。 347 | 是的,就是这样。 348 | 还有互动版本,例如HTOP是一个互动版本。 349 | 因此,NCDU是一个互动版本,让我可以浏览文件夹,我可以快速看到哦,这是视频讲座之一的文件夹,我们可以看到有这四个文件,每个文件大小都接近9 GB,我可以通过此界面快速删除它们。 350 | 另一个很棒的工具是“LSOF”,它代表“打开文件列表”。 351 | 您可能遇到的另一种模式是,您知道某个进程正在使用文件,但您不知道确切是哪个进程在使用该文件。 352 | 或者,类似地,某个进程正在侦听端口,但是,您又如何找出它是哪个进程呢? 353 | 那么让我们来举个例子。 354 | 我们在444端口上运行一个Python HTTP服务器。 355 | 也许我们不知道它正在运行,但是我们可以使用……我们可以使用LSOF。 356 | 是的,我们可以使用LSOF,而LSOF将打印大量信息。 357 | 您需要SUDO权限,因为它将要求谁拥有所有这些项。 358 | 由于我们只关心在此444端口上监听的进程,因此我们可以使用grep查询。 359 | 然后我们可以看到,有一个Python进程,具有此标识符,正在使用该端口,然后我们可以将其终止,从而终止该进程。 360 | 同样,有许多不同的工具可供使用。 361 | 甚至有用于进行所谓基准测试的工具。 362 | 因此,在Shell工具和脚本讲座中,我说过对于某些任务,“fd”比“find”快得多。 363 | 但是,如何检查呢?我可以使用“hyperfine”来测试它,这里有两个命令:一个使用“fd”仅搜索JPEG文件,另一个使用“find”执行相同的操作。 364 | 如果我执行它们,它会对这些脚本进行基准测试,并向我提供有关“fd”相对于“find”快多少的一些输出。 365 | 因此,我认为这种总览已经讲完了。 366 | 我知道有很多不同的主题和不同的视角来处理这些事情,但是我想再次强调的是,您不需要成为所有这些主题的专家,而是要意识到所有这些东西的存在。 367 | 因此,如果您遇到这些问题,您不需要重新发明轮子,而是可以重用其他程序员所做的所有工作。 368 | 鉴于此,我很乐意回答与本节或讲座中任何内容有关的问题。 369 | 有没有什么办法来大概估算程序需要多长时间?如果它运行时间较长,您是否应该担心?或者根据您的进程,您应该再等十分钟再开始查找为什么它运行时间这么长? 370 | 好的,了解一个程序需要运行多长时间的任务是非常难以确定的,这将取决于程序的类型。 371 | 这取决于您是在进行HTTP请求还是在读取数据......你可以做的一件事是,如果你知道你需要从内存或磁盘中读取两个千兆字节的数据,并将其加载到内存中,你可以进行快速的计算。 372 | 因为事情是这样安排的,所以那应该不会超过X秒。 373 | 或者,如果您从网络读取某些文件,并且知道网络链接是什么,并且它们花费的时间比您预期的要长五倍,则可以尝试这样做。 374 | 否则,如果您不是很确定。 375 | 例如,如果您在代码中尝试进行某些数学操作,并且不确定需要多长时间,您可以使用类似于日志记录的方式,并尝试打印中间阶段,以了解需要执行多少次操作以及三次迭代花费了十秒钟,那么在我的情况下,这将需要更长时间才能完成。 376 | 所以,我认为有很多方法可以解决这个问题,这又将取决于任务,但是,鉴于我们看到的所有工具,我们可能有几种很好的方法来开始解决这个问题。 377 | 还有其他问题吗?您还可以运行HTOP并查看是否有任何正在运行的东西。 378 | 如果您的CPU使用率为0%,那么可能出了问题。 379 | 好的。 380 | 今天的课程有很多练习,所以请随意选择您感兴趣的练习。 381 | 我们今天还将举行办公时间,只是提醒一下,办公时间。 382 | 您可以来问任何关于任何课程的问题。 383 | 我们不会期望您在几分钟内完成练习。 384 | 他们需要很长时间才能完成,但是我们将在那里回答关于以前课程的任何问题,甚至与练习无关的问题。 385 | 例如,如果您想了解如何使用TMUX以快速在窗格之间切换,任何想到的问题。 386 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture05_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 好的,大家都能听到我讲话吗?好的,欢迎回来。 2 | 我想谈一些行政事务。 3 | 第一周结束后,我们发了一封电子邮件通知你们我们已经上传了第一周的视频,所以你们可以在网上找到它们。 4 | 它们有我们做的所有屏幕录像,所以你们可以回头看看,如果你们对我们快速进行的某些事情感到困惑,可以随时问我们任何问题,如果讲义中的任何内容不清楚的话。 5 | 我们还发了一份调查问卷,所以你们可以向我们提供反馈,告诉我们哪些地方不清楚,哪些地方需要更详细的解释,或者其他任何问题,如果你们发现练习太难或太容易,请在那个网址里留言,我们会非常感激得到这些反馈,因为这将使课程在剩下的讲座和未来的课程迭代中变得更好。 6 | 这些事情说完了,我们将尽量及时上传视频,不想等到一周的结束才上传,所以请持续关注。 7 | 这些都说完了,现在我要开始这次讲座了,它叫做命令行环境,我们会涵盖一些不同的话题。 8 | 所以我们要涵盖的主要话题,你们可以记住,可能在这里更好,以便跟上我的讲话。 9 | 第一个是作业控制,第二个是终端复用器。 10 | 然后我会解释什么是点文件以及如何配置你的 shell。 11 | 最后,如何高效地与远程机器工作。 12 | 所以如果有什么不清楚的地方,请记住这些结构。 13 | 它们在某种程度上相互交互,涉及如何使用你的终端,但它们是一些相对独立的话题,所以请记住这一点。 14 | 那么我们就来看一下作业控制。 15 | 到目前为止,我们一直以一种很单一的方式使用 shell,比如,你执行一个命令,然后命令执行,然后你得到一些输出,这就是你可以做的一切。 16 | 如果你想运行几个东西,那就不清楚该怎么做。 17 | 或者如果你想停止一个程序的执行,那又该怎么做呢?我们可以用一个叫做 sleep 的命令来演示这一点。 18 | Sleep 是一个带有参数的命令,这个参数将是一个整数,它会睡眠。 19 | 它会在后台睡眠这么多秒。 20 | 所以如果我们做像 sleep 20 这样的事情 21 | 如果我们执行类似于 sleep 20 的命令,该进程将会休眠 20 秒钟。 22 | 但我们不想等待 20 秒钟才能完成命令。 23 | 因此,我们可以输入 "Ctrl+C"。 24 | 通过输入 "Ctrl+C",我们可以看到,在这里,终端通知我们,这是我们在编辑器 / Vim 讲座中介绍过的语法的一部分,我们输入了 "Ctrl+C",它停止了进程的执行。 25 | 实际上,这里正在使用一种名为信号的 UNIX 通信机制。 26 | 当我们键入 "Ctrl+C" 时,终端为我们发送了一个名为 SIGINT 的信号,该信号表示信号中断,告诉程序停止自己。 27 | 还有许多这样的信号。 28 | 如果你输入 man signal,然后向下滚动一点,你就会看到它们的列表。 29 | 它们都有数字标识符,有一种短名称和描述。 30 | 例如,我刚刚描述的信号在这里,编号为 2,是 SIGINT。 31 | 这是终端在想要中断程序执行时发送给程序的信号。 32 | 还有一些信号需要熟悉,例如 SIGQUIT,如果你从终端工作并希望退出程序执行。 33 | 对于大多数程序,它们将执行相同的操作,但现在我们将展示一个不同的程序,这是将发送的信号。 34 | 有时候可能会有些混淆。 35 | 查看这些信号,例如,SIGTERM 在大多数情况下等同于 SIGINT 和 SIGQUIT,但它只是当它不通过终端发送时。 36 | 我们将要介绍的其他一些信号包括 SIGHUP,它表示终端出现了问题。 37 | 例如,当你在终端中运行程序时,如果你关闭终端,但终端中仍在运行一些程序,那么程序将发送该信号告诉所有进程应该关闭,就像命令行通信中出现了故障,它们现在应该关闭。 38 | 信号可以做更多的事情,而不仅仅是停止、中断程序并要求它们完成。 39 | 你可以使用 SIGSTOP 暂停程序的执行,然后使用 SIGCONT 命令在稍后的时间点继续执行程序。 40 | 由于所有这些可能有些抽象,让我们看一些例子。 41 | 首先,让我们展示一个 Python 程序。 42 | 我将非常快速地浏览一遍程序。 43 | 44 | 这是一个Python程序,像大多数Python程序一样,它正在导入这个信号库并在此处定义这个处理程序。 45 | 这个处理程序正在写入“哦,我收到了一个SIGINT,但我不会停在这里”。 46 | 之后,我们告诉Python,当它收到SIGINT时,我们希望这个程序停止。 47 | 程序的其余部分是一个非常愚蠢的程序,只会打印数字。 48 | 现在让我们看看它的运行过程。 49 | 我们运行Python SIGINT。 50 | 它正在计数。 51 | 我们尝试使用“Ctrl+C”来停止它,这会发送SIGINT,但程序实际上并没有停止。 52 | 这是因为程序在处理此异常的方式上有一个方法,我们不想退出。 53 | 如果我们发送SIGQUIT,也就是通过“Ctrl+\”完成的,我们可以看到,由于程序没有处理SIGQUIT的方法,它会执行默认操作,即终止程序。 54 | 例如,如果有人Ctrl+C了您的程序,您的程序应该执行某些操作,例如保存程序的中间状态到文件中,以便以后恢复,您可以使用此功能。 55 | 这就是您可以编写此类处理程序的方法。 56 | 您能再重复一遍问题吗?当它停止时,您输入了什么?所以我...我输入了“Ctrl+C”尝试停止它,但它没有停止,因为SIGINT被程序捕获了。 57 | 然后我输入了“Ctrl+”,这会发送一个不同的信号SIGQUIT,而这个信号没有被程序捕获。 58 | 值得一提的是,有一些信号是软件无法捕获的。 59 | 例如,有一些信号像SIGKILL是不能被捕获的。 60 | 如果这样做,它将终止进程的执行,无论如何。 61 | 这有时可能是有害的。 62 | 因为这可能会留下孤儿进程。 63 | 例如,如果一个进程有其他小孩子进程,它会启动,而你SIGKILL它,所有这些进程都会继续在那里运行,但它们将没有父进程,你可能会遇到非常奇怪的行为。 64 | 如果我们注销,程序会收到什么信号?如果您注销?那将是...例如,如果您在SSH连接中关闭连接,那就是挂起信号SIGHUP,我将在一个例子中介绍。 65 | 这就是会被发送的信号。 66 | 您可以编写一个包装器,忽略该信号,以使该进程在关闭时继续工作。 67 | 68 | 让我们展示一下使用停止和继续功能能做什么。 69 | 例如,我们可以启动一个非常长的进程。 70 | 让我们睡眠一千秒,这将花费很长时间。 71 | 我们可以按"Ctrl+Z"组合键控制进程,如果我们这样做,我们可以看到终端显示"已暂停"。 72 | 这实际上意味着该进程被发送了一个SIGSTOP信号,现在仍然在那里,您可以继续执行它,但现在它完全停止了,并且在后台,我们可以启动一个不同的程序。 73 | 当我们尝试运行这个程序时,请注意我在末尾包含了一个"&"。 74 | 这告诉bash我希望该程序在后台开始运行。 75 | 这与在shell中运行程序的所有这些概念有关,但是是后台运行的。 76 | 程序将开始运行,但不会接管我的提示符。 77 | 如果我只是运行此命令而没有这个符号,我将无法执行任何操作。 78 | 在命令完成或我突然结束它之前,我将无法访问提示符。 79 | 但如果我这样做,它会说"有一个新的进程是这个"。 80 | 这是进程的标识号,我们现在可以忽略它。 81 | 如果我键入"jobs"命令,我会得到输出,表示我有一个已暂停的工作,即"sleep 1000"工作。 82 | 然后我有另一个正在运行的工作,即"NOHUP sleep 2000"。 83 | 假设我想继续第一个工作。 84 | 第一个工作已暂停,不再执行。 85 | 我可以通过"BG %1"命令继续执行。 86 | "%"是指我想引用这个特定的进程。 87 | 现在,如果我这样做并查看工作,现在这个工作再次在运行。 88 | 现在两个都在运行。 89 | 如果我想停止所有这些,我可以使用"kill"命令。 90 | "kill"命令用于杀死工作,即停止它们,直观地说,但实际上非常有用。 91 | "kill"命令允许您发送任何类型的Unix信号。 92 | 例如,在这里,我们不完全杀死它,而是发送一个停止信号。 93 | 我将发送一个停止信号,这将再次暂停该进程。 94 | 我仍然必须包含标识符,因为没有标识符,shell将不知道是停止第一个还是第二个。 95 | 现在它说已被暂停,因为有一个信号被发送。 96 | 如果我再次键入"jobs"命令,我们可以看到第二个正在运行,而第一个已被停止。 97 | 98 | 回到之前的一个问题,如果你关闭终端窗口,会发生什么?为什么有时候人们会建议在远程会话中运行作业之前使用 NOHUP 命令?这是因为如果我们尝试向第一个作业发送挂起命令,类似于其他信号,它会将其挂起,这将终止该作业。 99 | 第一个作业不再存在,而第二个作业仍在运行。 100 | 然而,如果我们尝试向第二个作业发送信号,如果我们现在关闭终端窗口,它仍将继续运行。 101 | 就像 NOHUP 一样,它将所执行的命令封装起来,忽略任何挂起信号,并使其继续运行。 102 | 如果我们向第二个作业发送"kill"信号,那么它将无法被忽略,这将杀死该作业,无论如何。 103 | 我们就没有任何作业了。 104 | 这种情况下的工作控制部分就讲完了。 105 | 到目前为止有没有什么问题?有没有什么没有讲清楚的地方?什么是 BG?BG有两个命令。 106 | 当你的作业被后台运行并停止时,你可以使用 BG (短语"background") 命令来继续在后台运行该进程。 107 | 这相当于向其发送一个继续信号,使其继续运行。 108 | 然后还有另一个命令叫做 FG,如果你想将其恢复到前台并重新连接标准输出。 109 | 好的,作业控制是很有用的,通常情况下,当你需要在一个窗口中编辑器和另一个窗口中程序,并且可能要监视标签中的资源消耗时,你需要做的实际上是打开更多的窗口。 110 | 我们可以一直打开终端窗口。 111 | 但事实是,这种情况有更方便的解决方案,这就是终端复用器的作用。 112 | 像tmux这样的终端复用器可以让你创建不同的工作空间,让你可以快速地重新排列环境,而且它有很多功能。 113 | 它可以让你有不同的会话。 114 | 还有一个更...古老的命令,叫做"screen",它可能更容易获得。 115 | 116 | 我认为这个概念可以推广到两个方面。 117 | 我们推荐学习 tmux,实际上我们有相应的练习。 118 | 现在我将展示一个不同的场景。 119 | 我讲话时...哦,让我做个简短的笔记。 120 | tmux 有三个核心概念,我会详细讲解。 121 | 主要思想是有所谓的“会话(session)”,会话包含“窗口(window)”,而窗口包含“窗格(pane)”。 122 | 记住这个层次结构将会很有用。 123 | 你可以将“窗口(window)”基本上等同于其他编辑器和浏览器中的“标签(tabs)”。 124 | 我将介绍功能,主要是你在不同级别上能做什么。 125 | 首先,当我们运行 tmux 时,它会启动一个会话。 126 | 在这里,现在似乎什么都没有改变,但现在正在发生的是我们在一个与之前不同的 shell 中。 127 | 因此,在我们的 shell 中,我们启动了一个进程,即 tmux,然后 tmux 启动了一个不同的进程,即我们当前所在的 shell。 128 | 这个很好的一点是,这个 tmux 进程与原始 shell 进程是分开的。 129 | 在这里,我们可以做一些事情。 130 | 例如,我们可以执行“ls -la”,查看里面发生了什么。 131 | 然后我们可以开始运行我们的程序,它将在其中运行,我们可以使用“Ctrl+A d”来分离会话。 132 | 如果我们使用“tmux a”,那么我们会重新连接到会话。 133 | 我们离开了计算数字的非常愚蠢的 Python 程序,并且如果我们 tmux...嘿,进程仍然在那里运行。 134 | 我们可以关闭这个终端并打开一个新的终端,我们仍然可以重新连接,因为这个 tmux 会话仍在运行。 135 | 再次说明,在 tmux 中,与 Vim 不同,它没有模式的概念,而是采用了更类似于 Emacs 的方式,即几乎每个 tmux 命令都可以通过其命令行输入。 136 | 但我建议你熟悉键绑定。 137 | 一开始可能有点难以理解,但一旦你熟悉了它们... 138 | "Ctrl+C",是的,当你熟悉了它们之后,你会比使用命令更快地使用键绑定。 139 | 关于键绑定的一个注意点:所有的键绑定都有一个形式,就像你输入一个前缀,然后再按一些键。 140 | 例如,要分离,我们要按下“Ctrl+A”,然后再按“D”。 141 | 这意味着你首先按下“Ctrl+A”,然后松开它,再按“D”来分离。 142 | 在默认的tmux中,前缀是“Ctrl+B”,但你会发现大多数人将其重新映射为“Ctrl+A”,因为在键盘上更加人性化。 143 | 你可以在练习中了解更多关于如何进行这些操作的信息,我们会链接到基础知识和如何对tmux进行某些生活质量的修改。 144 | 回到会话的概念,我们可以通过像“tmux new”这样的命令创建一个新的会话,我们可以给会话命名。 145 | 所以我们可以像“tmux new -t foobar”这样做,这是一个完全不同的会话,我们已经开始了。 146 | 我们可以在这里工作,我们可以从中分离出来。 147 | “tmux ls”会告诉我们我们有两个不同的会话:第一个是命名为“0”,因为我没有给它命名,第二个被称为“foobar”。 148 | 我可以附加到foobar会话,也可以结束它。 149 | 这非常好,因为有了这个,你可以在完全不同的项目中工作。 150 | 例如,有两个不同的tmux会话和不同的编辑器会话,不同的进程正在运行......当你在一个会话中时,我们从窗口的概念开始。 151 | 这里我们有一个单独的窗口,但是我们可以使用“Ctrl+A c”(用于“创建”)来打开一个新窗口。 152 | 这里没有执行任何操作。 153 | 它的作用是,tmux为我们打开了一个新的shell,我们可以在这里开始运行另一个这些程序之一。 154 | 为了快速跳转标签,我们可以按“Ctrl+A”和“previous”、“p”(表示“previous”)来上一个窗口。 155 | “Ctrl+A”和“next”来到下一个窗口。 156 | 你也可以使用数字。 157 | 所以如果我们开始打开很多这些标签,我们可以使用“Ctrl+A 1”来特定地跳转到编号为“1”的窗口。 158 | 最后,也很有用的是有时你可以重新命名它们。 159 | 例如,这里我正在执行这个Python进程,但那可能并不是真正有意义的,我想......我可能想要有像“execution”之类的东西,这样就可以重新命名该窗口的名称,以便你可以整理得非常整齐。 160 | 161 | 这样做并不能满足你想要在终端中同时拥有两个东西的需求,比如在同一个显示屏中。 162 | 这就是窗格存在的原因。 163 | 现在,我们有一个只有一个窗格的窗口(到目前为止,我们打开的所有窗口都只有一个窗格)。 164 | 但是如果我们按下“Ctrl+A",然后按下引号键,这将把当前显示屏分成两个不同的窗格。 165 | 你看,我们打开的下面的那个是与上面的不同的shell,我们可以在这里运行任何进程。 166 | 我们可以继续分裂,如果我们按下“Ctrl+A %",那么就会垂直分裂。 167 | 你可以使用许多不同的命令来重新排列这些选项卡。 168 | 其中一个我发现非常有用的命令是,当你开始时感到有些沮丧,需要重新排列它们。 169 | 在我解释之前,要移动这些窗格,这是你想一直做的事情,你只需按下“Ctrl+A"和箭头键,这样就可以快速浏览不同的窗口并再次执行操作……我正在做很多“ls -a”,我可以做“HTOP”,我们将在调试和分析讲座中解释这个。 170 | 我们可以浏览它们,就像重新排列它们一样,还有一堆命令,你可以在练习中学习。 171 | “Ctrl+A" space非常棒,因为它会将当前的选项卡等距分布,并让你通过不同的布局。 172 | 有些对于我的当前终端配置来说太小了,但我认为这覆盖了大部分内容。 173 | 哦,还有,例如,我们已经开始执行的Vim,对于当前的tmux窗格来说太小了。 174 | 因此,相对于拥有多个终端窗口,一个非常方便的事情就是可以在tmux中放大它,你可以通过按下“Ctrl+A z”来要求“缩放”。 175 | 它会扩展窗格以占据所有的空间,然后再按下“Ctrl+A z”即可返回。 176 | 对于终端多路复用器或tmux具体来说有什么问题吗?是在运行所有相同的东西吗?在不同的窗口中运行是否有任何执行差异?是的,它不会与在计算机中打开两个终端窗口有任何区别。 177 | 当然,当它到达CPU时,这将再次进行多路复用。 178 | 就像有一个时间共享机制,但没有任何区别。 179 | tmux通过为您提供这个可以快速操作的可视化布局,使使用变得更加方便。 180 | 181 | 其中一项主要优势将在我们到达远程机器时体现,因为您可以留下其中之一,我们可以从其中一个tmux系统中分离出来,关闭连接,即使我们关闭连接并且终端将发送挂起信号,也不会关闭所有已启动的tmux。 182 | 还有其他问题吗?让我禁用按键记录器。 183 | 现在我们要进入dotfiles的主题,以及一般如何配置您的shell以执行您想要执行的任务,主要是如何更快地执行它们并以更方便的方式执行。 184 | 我将首先使用别名来激励大家。 185 | 所谓别名,就是现在,您可能正在开始做很多次的事情,例如,我只想LS一个目录,并将所有内容以列表格式和可读格式显示出来。 186 | 这很好。 187 | 它不是那么长的命令。 188 | 但是,随着您构建越来越长的命令,反复输入它们可能会变得有点麻烦。 189 | 这就是别名有用的原因之一。 190 | 别名是一个将成为您shell内置的命令,它将重新映射一个短字符序列到一个长序列。 191 | 例如,如果我执行alias ll="ls -lah",这将使用这个参数调用"alias"命令,LS将更新我的shell环境以了解这个映射。 192 | 因此,如果我现在执行LL,它将在不必键入整个命令的情况下执行该命令。 193 | 它可以非常方便,有很多很多用途。 194 | 需要注意的一件事是,这里的alias与其他命令没有什么特殊之处,它只是接受单个参数。 195 | 在等号周围没有空格,这是因为别名只接受一个参数,如果您尝试执行类似于这样的操作,那么就会给它更多的参数,那是不起作用的,因为那不是它期望的格式。 196 | 除此之外,别名还有其他用例,例如,对于某些事情来说,使用别名可能更方便,比如我的最爱之一就是git status。 197 | 它非常长,我不喜欢经常键入那么长的命令,因为您最终会花费很多时间。 198 | 因此,GS将替换为执行git status。 199 | 您还可以使用别名将经常打错的命令别名为自己,但加上默认标志。 200 | "sl=ls"就是一个例子。 201 | 202 | 其他有用的映射方式是,你可能想将一个命令别名为带有默认标志的自身。 203 | 因此,我现在要创建一个别名,它是对"move"命令的别名,即"MV",并将其别名为相同的命令,但添加了"-i"标志。 204 | 如果你查看man页面,你会发现"-i"标志代表"交互式"。 205 | 这将在我覆盖文件之前提示我。 206 | 一旦我执行了这个命令,我就可以像这样移动"aliases"到"case"。 207 | 默认情况下,"move"命令不会询问,如果"case"已经存在,它将被覆盖。 208 | 那很好,我要覆盖那里的任何东西。 209 | 但现在,"move"已经被扩展为"move -i",并使用它来询问我"你确定要覆盖它吗?"我可以选择不覆盖,以避免丢失文件。 210 | 最后,你可以使用"alias move"命令来查询这个别名的含义。 211 | 这样可以快速确定你实际执行的命令是什么。 212 | 例如,有一个不方便的部分是,如果你有别名,那么如何将它们持久化到你当前的环境中呢?如果我现在关闭这个终端,所有这些别名都会消失。 213 | 你不想一遍又一遍地重新输入这些命令。 214 | 更一般地说,如果你开始配置你的shell,你需要某种方式来引导所有这些配置。 215 | 你会发现大多数shell命令程序都使用一些基于文本的配置文件。 216 | 由于历史原因,我们通常称其为"dotfiles",因为它们以点号开头。 217 | 所以对于我们的shell,比如bash,我们可以查看bashrc文件。 218 | 为了演示目的,在这里我一直在使用ZSH,这是一个不同的shell,我将会配置bash,并启动bash。 219 | 所以如果我在这里创建一个条目,我说SL映射到LS,我已经修改了它,现在我启动bash。 220 | Bash现在是完全未配置的,但现在如果我输入SL...嗯,这是意外的。 221 | 哦,好的。 222 | 那很重要,你的配置文件要放在你的主文件夹中。 223 | 因此,bash的配置文件将位于"~"中,它会扩展到你的主目录,然后是bashrc。 224 | 在这里,我们可以创建别名,不仅可以使用别名,还可以有很多其他的配置。 225 | 226 | 现在我们开始一个Bash会话并运行SL。 227 | 现在它已经加载了,在Bash程序启动时加载了所有的配置。 228 | 例如,在这里,我有一个相当无用的提示符。 229 | 它只告诉我使用的shell名称是Bash,版本是5.0。 230 | 我不想显示它,而且在您的shell中,像许多其他东西一样,这只是一个环境变量。 231 | 因此,“PS1”只是您提示符的提示字符串,我们实际上可以修改它,使其成为一个“>”符号。 232 | 现在已经修改过了,我们已经有了这个。 233 | 但是,如果我们退出并再次调用Bash,这个修改就会丢失。 234 | 但是,如果我们添加这个条目并说,我们希望“PS1”是这样的,然后再次调用Bash,这个修改就会被保留下来。 235 | 我们可以继续修改这个配置。 236 | 所以也许我们想包含我们正在工作的目录,这告诉我们我们在其他shell中所看到的相同的信息。 237 | 还有许多许多选项,Shell是高度可配置的,不仅是通过这些文件配置Shell,还有许多其他程序。 238 | 就像我们在编辑器讲座中看到的那样,Vim也是通过这种方式进行配置的。 239 | 我们给你这个vimrc文件,并告诉你将它放在home/.vimrc下,这是相同的概念,但只是针对Vim。 240 | 它只是在启动时给它一组指令,以便您可以保持所需的配置。 241 | 甚至许多非...许多程序也支持这种方式。 242 | 例如,我的终端仿真器,这是另一个概念,是运行shell的程序,并将其显示在计算机屏幕上。 243 | 它也可以通过这种方式进行配置,因此如果我修改它,我可以更改字体的大小。 244 | 例如,现在,我已经为演示目的增加了字体大小,但如果我更改这个条目并使其为28,然后写入这个值,你会发现字体的大小已经改变了,因为我编辑了这个文本文件,它指定了我的终端仿真器应该如何工作。 245 | 还有其他问题关于点文件吗?好的,知道有这么多的配置可能会让人感到有些不知所措,是吗? 246 | 你如何学习关于哪些内容可以进行配置?好消息是,我们在讲义中已经为你提供了非常好的资源链接。 247 | 但是主要思想是很多人喜欢配置这些工具,他们把自己的配置文件上传到了GitHub这样的在线库中。 248 | 例如,在GitHub上搜索“dotfiles”,你会发现有成千上万的人分享他们的配置文件。 249 | 我们的课程导师也链接了我们的dotfiles。 250 | 因此,如果你真的想知道我们设置中的任何部分是如何工作的,你可以查看这些文件并尝试弄清楚。 251 | 你也可以随意向我们询问。 252 | 例如,如果我们转到这个存储库,我们会发现有很多文件可以配置。 253 | 例如,有一个用于bash的文件,前几个用于git,这些可能会在版本控制讲座中进行介绍。 254 | 如果我们例如转到bash配置文件,它是一种不同形式的bashrc,它非常有用,因为你可以通过查看手册页来学习,但是手册页通常只是所有不同选项的描述性解释,有时通过看其他人的示例并尝试理解他们为什么这样做以及它如何帮助他们的工作流程更有帮助。 255 | 我们可以看到这里的人已经进行了不区分大小写的文件名扩展操作。 256 | 我们在shell脚本和工具中介绍了扩展操作。 257 | 这里你说不,我不想区分大小写,只需在shell中设置此选项即可使这些操作按照你期望的方式运行。 258 | 类似地,还有一些别名。 259 | 在这里,你可以看到这个人正在做很多别名。 260 | 例如,“d”代表“Dropbox”,因为这样更短。 261 | “g”代表“git”……例如我们来看一下vimrc。 262 | 通过查看它并尝试提取有用信息,这实际上可以非常有信息量。 263 | 我们不建议只是将一个巨大的配置文件复制到你的文件中,因为可能看起来更漂亮,但你可能真的不了解其中的情况。 264 | 最后,关于dotfiles我想提到的一件事是,人们不仅试图将这些文件推送到GitHub中以便其他人阅读,这是其中一个原因。 265 | 他们还确保他们可以重现他们的设置。 266 | 为此,他们使用了各种不同的工具。 267 | 例如GNU Stow,他们将所有的dotfiles放在一个文件夹中,并使用一种名为符号链接的工具欺骗系统,让它们看起来是实际的文件。 268 | 269 | 我来快速画一下我所说的意思。 270 | 通常的文件夹结构可能是这样的:你有你的主文件夹,在这个主文件夹中可能会有你的bashrc,它包含了你的bash配置;你可能会有你的vimrc,如果你能把它们放在版本控制下就太好了。 271 | 但是问题是,你可能不想在你的主文件夹中放置一个git仓库(关于git的内容明天会讲到)。 272 | 所以人们通常会创建一个dotfiles仓库,然后在这里为他们的bashrc和vimrc创建条目。 273 | 这其实就是文件所在的地方,他们只是告诉操作系统,无论何时有人想要读取这个文件或者写入这个文件,都把这个请求转发到另一个文件。 274 | 这是一个叫做符号链接的概念,在这种情况下很有用,但是它通常是UNIX中一个非常有用的工具,我们在讲座中还没有涉及到,但是你应该熟悉。 275 | 通常情况下,语法是使用“ln -s”指定一个符号链接,然后把你想要创建的文件的路径和你想要创建的符号链接放在一起。 276 | 所有这些高级工具,我们在这里列出来的,它们都可以用一些技巧实现,这样你就可以把所有的点文件整齐地放在一个文件夹里,它们可以被版本控制,也可以被符号链接,这样其它程序可以在它们的默认位置找到它们。 277 | 关于点文件有什么问题吗?你需要在你的主文件夹和版本控制文件夹中都有点文件吗?例如,bash程序总是会查找"home/.bashrc"。 278 | 这就是程序要查找的位置。 279 | 当你创建一个符号链接时,你把你的"home/.bashrc"放在一个特殊的UNIX文件中,这个文件只是一个指针,它说:“无论何时你想读取这个文件,都要去找那个文件。 280 | ”这个文件没有内容,比如你的别名不是这个点文件的一部分。 281 | 通过这样做,你可以把你的其他文件放在那个文件夹中。 282 | 283 | 如果版本控制没有用处,那么想一想如果你想将它们放到Dropbox文件夹中,这样它们就可以同步到云端,这是另一种情况,这时符号链接可能非常有用,因此你不需要将dotfiles文件夹放在主目录中,对吧?因为你可以使用符号链接,指向其他位置。 284 | 只要你有一种方式让默认路径解析到你所在的位置就可以了。 285 | 哦,抱歉,有没有其他关于dotfiles的问题?最后我想在讲座中讨论一下与远程计算机的交互,这是你迟早会遇到的情况。 286 | 如果你知道一些东西,它们会让你在处理远程计算机时更加轻松。 287 | 也许现在因为你在使用Athena集群,但在你的编程生涯中,有一个非常普遍的概念,就是你有你的本地工作环境,然后有一些生产服务器实际上正在运行代码,因此熟悉如何在/与远程计算机一起工作非常重要。 288 | 因此,处理远程计算机的主要命令是SSH。 289 | SSH就像一个安全的Shell,它将负责到达我们想要到达的地方或者告诉它去打开一个会话。 290 | 这里的语法是:“JJGO”是我想要在远程机器上使用的用户,这是因为该用户与我本地机器上的用户不同,这在很多情况下都是这样,然后“@”告诉终端这个符号分离了用户和地址。 291 | 这里我使用的是一个IP地址,因为我实际上在我的计算机中有一个虚拟机,这是远程的。 292 | 我将要通过SSH连接它。 293 | 这是我使用的URL,抱歉,我使用的IP,但是你也可能会看到类似这样的东西:“我想以‘JJGO’在‘foobar.mit.edu’上SSH”。 294 | 如果你使用一些具有DNS名称的远程服务器,这可能是更常见的。 295 | 回到常规命令,我们尝试SSH,它会要求我们输入密码,这是非常常见的事情。 296 | 现在我们在那里。 297 | 我们仍然在我们同一个终端模拟器中,但现在SSH会将整个虚拟显示器转发到显示远程shell正在显示的内容。 298 | 299 | 我们可以在这里执行命令并查看远程文件。 300 | 关于SSH,有一些方便的事情在数据整理讲座中简要介绍过,它不仅仅适用于打开连接,还可以让你远程执行命令。 301 | 例如,如果我这样做,它会再次询问我的密码。 302 | 它会执行这个命令,然后回到我的终端,并通过当前单元格的标准输出将该命令的输出,也就是远程机器的输出,传输过来。 303 | 我可以将其放入管道中,并在本地管道中继续工作。 304 | 到目前为止,必须输入密码有点不方便。 305 | 有一个很好的技巧可以解决这个问题。 306 | 它就是我们可以使用称为“SSH密钥”的东西。 307 | SSH密钥使用公钥加密来创建一对SSH密钥,一个公钥和一个私钥,然后您可以将公钥部分给服务器。 308 | 因此,您复制公钥,每当您尝试进行身份验证时,它将使用私钥向服务器证明您实际上是您所说的人。 309 | 我们可以快速展示如何做到这一点。 310 | 现在我没有任何SSH密钥,所以我要创建一对。 311 | 第一件事,它会问我想让这个密钥存储在哪里。 312 | 毫不意外,它正在执行这个操作。 313 | 这是我的主文件夹,然后它使用这个“.ssh”路径,这是我们之前提到过的点文件的概念。 314 | 像“.ssh”是一个包含许多配置文件的文件夹,用于设置SSH的行为方式。 315 | 因此,它会要求我们输入一个密码。 316 | 这个密码是用来加密密钥的私钥部分的,因为如果有人得到了您的私钥,如果您没有受到密码保护的私钥,如果他们得到了那个密钥,他们可以在任何服务器上冒充您。 317 | 而如果您添加了密码,他们必须知道密码才能实际使用密钥。 318 | 它创建了一个“keeper”。 319 | 我们可以检查这两个文件现在是否在ssh下,并且我们可以看到...我们有这两个文件:我们有25519和公钥。 320 | 321 | 如果我们通过输出 "cat" 命令查看这个密钥,那么这个密钥实际上不是什么复杂的二进制文件,它只是一个文本文件,里面包含了公钥和一些别名,这样我们就知道这个公钥是什么。 322 | 我们可以通过将这个文件复制到 ".ssh/authorized_keys" 文件中来告诉服务器我们有 SSH 授权。 323 | 这里我正在使用 "cat" 命令来查看这个文件的输出,它只是一个我们想要复制的文本行,然后将其传输到 SSH 中,然后远程使用 "tee" 命令将标准输入的内容倾倒到 ".ssh/authorized_keys" 中。 324 | 如果我们这样做,显然会要求我们输入密码。 325 | 复制成功后,我们可以检查一下,如果再次尝试 SSH,它会首先要求我们输入口令,但是您可以安排在会话中保存口令,这样我们就不必为服务器输入密钥。 326 | 我可以再次演示一下。 327 | 还有更多有用的内容。 328 | 哦,我们可以做...如果那个命令看起来有点奇怪,您实际上可以使用专门为此构建的命令,这样您就不必构建这个 "ssh t" 命令了。 329 | 这个命令就叫做 "ssh-copy-id"。 330 | 我们可以做同样的事情,它会复制密钥。 331 | 现在,如果我们尝试 SSH,我们可以在不输入任何密钥或密码的情况下进行 SSH。 332 | 还有更多内容。 333 | 我们可能想要复制文件。 334 | 您不能使用 "CP" 命令,但可以使用 "SCP" 命令,即 "SSH 复制"。 335 | 在这里,我们可以指定要复制的本地文件名为 "notes",语法有些相似。 336 | 我们要将它复制到远程位置,然后使用分号分隔要复制到的路径。 337 | 然后我们指定要将其复制为 "notes",但我们也可以将其复制为 "foobar"。 338 | 如果我们这样做,它将被执行,并告诉我们所有的内容都已经被复制到那里了。 339 | 如果您要复制很多文件,那么您应该使用一个更好的命令,它叫做 "RSYNC"。 340 | 例如,在这里仅通过指定这三个标志,我告诉 RSYNC 在可能的情况下保留所有权限,以尝试检查文件是否已经被复制。 341 | 342 | 例如,SCP将尝试复制已经存在的文件。 343 | 例如,如果您在复制过程中断开连接,SCP会从头开始尝试复制每个文件,而RSYNC会从停止的地方继续。 344 | 在这里,我们要求它复制整个文件夹,它只需要很快地复制整个文件夹。 345 | 关于SSH的另一个需要知道的事情是,“SSH配置”是SSH的点文件的等效文件。 346 | 因此,如果我们编辑SSH配置文件,如果我将SSH配置文件编辑成这样,就不必每次都输入“ssh jjgo”这个非常长的字符串,这样我就可以引用特定的远程主机。 347 | 我想引用特定的用户名,我可以在这里加上一些内容,例如这是用户名,这是主机名,该主机所指的内容以及你应该使用这个身份验证文件。 348 | 如果我将此内容复制到本地文件夹中,我可以将其复制到ssh中。 349 | 现在,我只需要说我想要SSH连接到名为VM的主机。 350 | 通过这样做,它会从SSH配置中获取所有的配置并将其应用于这里。 351 | 这个解决方案比创建SSH别名之类的东西要好得多,因为像SCP和RSYNC这样的其他程序也知道SSH的点文件,并且在有时会使用它们。 352 | 关于远程机器,我最后要介绍的是在这里,例如,我们将有tmux,我们可以像之前说的那样开始编辑某个文件并开始运行某个作业。 353 | 例如,像HTOP这样的东西。 354 | 这正在运行,我们可以从中分离,关闭连接,然后再次SSH。 355 | 然后,如果您执行“tmux a”,一切都与您离开的状态一样,就好像什么也没有改变。 356 | 如果在后台有执行的事情,它们将继续执行。 357 | 我想这就是我要讲的有关这个工具的所有内容。 358 | 与远程机器有关的任何问题?这是一个非常好的问题。 359 | 我为此做了什么。 360 | 361 | 哦,是的,抱歉。 362 | 问题是,如何在本地机器上使用tmux,同时又要在远程机器上使用tmux?有几个处理这个问题的技巧。 363 | 第一个是更改前缀。 364 | 例如,我在本地机器上将前缀从“Ctrl+B”更改为“Ctrl+A”,然后在远程机器上仍为“Ctrl+B”。 365 | 这样我就可以在本地tmux上执行操作,使用“Ctrl+A”,在远程tmux上执行操作,使用“Ctrl+B”。 366 | 另一件事是你可以有不同的配置文件,所以我可以做类似于这样的事情......哦,因为我没有自己的ssh配置文件。 367 | 但如果你......嗯,我可以SSH“VM”。 368 | 这里,你看到的两个条之间的差异,例如,是因为tmux配置不同。 369 | 就像你将在练习中看到的那样,tmux配置在tmux.conf文件中,你可以在tmux.conf中做很多事情,比如根据你所在的主机更改颜色,以便快速获得视觉反馈,或者如果你有一个嵌套会话。 370 | 此外,如果你在同一台主机上,并且尝试在一个tmux会话中使用tmux,tmux会阻止你这样做,以避免出现问题。 371 | 如果有关我们涵盖的所有主题的其他问题,请随意问。 372 | 对于那个问题的另一个答案是,如果你将前缀键击两次,它会将其发送一次到底层shell。 373 | 因此,本地绑定是“Ctrl+A”,远程绑定是“Ctrl+A”,你可以键入“Ctrl+A”、“Ctrl+A”,然后键入“D”,例如从远程分离。 374 | 我想今天的课程到此结束了,有很多与所有这些主要主题相关的练习,我们今天也将进行办公时间。 375 | 所以随时来问我们任何问题。 376 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture08_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 大家好,让我们开始下一堂课。 2 | 今天我们将解决元编程的主题,这个标题有点奇怪。 3 | 我们并没有完全清楚元编程的含义。 4 | 我们在命名这个讲座时想不出更好的名字,因为这个讲座是关于你在使用软件时所进行的工作周围的过程,不一定是编程本身,而是关于这个过程的内容。 5 | 这可能涉及到如何构建你的系统、如何进行测试、如何添加软件依赖等等,这些内容在构建较大的软件时变得非常重要,但它们本身并不是编程。 6 | 所以,我们这堂课首先要讨论的是构建系统的概念。 7 | 有多少人使用过构建系统或知道它是什么?好的,大约有一半的人。 8 | 那么对于其余的人来说,构建系统的核心思想是,你正在编写一篇论文、编写软件、或者在学习某个课程,无论是哪种情况,你都有一堆命令,你可能把它们写在你的 shell 历史记录中,或者在某个文档中记下来,你知道如果想要完成特定的任务,必须运行这些命令序列。 9 | 例如,有一系列的命令需要按顺序运行,以便构建你的论文或论文的 PDF 版本,或者只是运行当前课程的测试。 10 | 构建系统的想法是,你想把这些规则编码成一个工具,这个工具可以为你完成这些任务。 11 | 特别是,你要教这个工具了解构建不同工件之间的依赖关系。 12 | 有许多不同类型的这种工具,许多是针对特定目的、特定语言构建的。 13 | 有些是用于构建论文,有些是用于构建软件,有些是用于特定的编程语言,比如 Java 或某些其他的语言。 14 | 有些工具甚至内置了构建工具,例如 NPM,如果你做过 Node.js 开发,可能就知道它具有跟踪依赖项和构建依赖项的内置工具。 15 | 但更一般地说,这些被称为构建系统,在它们的核心中,它们都有一些共同的功能。 16 | 它们有许多目标,这些目标是你想要构建的东西,比如 paper.pdf,但它们也可以是更抽象的东西,比如运行测试套件或构建程序的二进制文件。 17 | 然后,你有一堆依赖项,依赖项是需要构建的东西,以便构建这个目标 18 | 然后你有一些规则,规定如何从完整的依赖项列表中得到目标文件。 19 | 举个例子,如果要构建我的paper.pdf,我需要一些绘图图像,它们将被嵌入到论文中,因此需要构建它们。 20 | 但是一旦它们被构建,如何使用这些文件来构建论文呢?这就是规则的作用。 21 | 它是一系列命令,你需要运行它们才能完成从一个文件到另一个文件的转换。 22 | 如何编码这些规则因不同的工具而异。 23 | 在这门课程中,我们将关注一种叫做make的工具。 24 | make是一种几乎可以在今天登录的任何系统上找到的工具。 25 | 比如,它会在Mac OS上,基本上所有的Linux和BSD系统上找到,而且你也可以很容易地在Windows上获取它。 26 | 它不适用于非常复杂的软件,但对于任何简单到中等复杂度的东西都可以很好地工作。 27 | 当你在命令行上运行make命令时,如果目录为空并且没有指定目标并且找不到makefile,它就会停止。 28 | 它会有益地告诉你已经停止运行,但也会告诉你找不到makefile。 29 | make将在当前目录中查找一个名为Makefile的文件,这就是你编码这些目标、依赖项和规则的地方。 30 | 所以,让我们试着写一个makefile吧。 31 | 假设我正在写这篇假想的论文,所以我要创建一个Makefile,在这个Makefile中,我将声明我的paper.pdf是由paper.tex、plotdata.png这两个文件构建而来,构建这个文件的命令是使用PDF latex命令。 32 | 对于那些不熟悉这种构建文档的方式的人来说,TeX是一种非常方便的文档编程语言。 33 | 它是一种非常难看的语言,使用起来很麻烦,但它可以产生很好的文档。 34 | 而将TeX文件转换成PDF的工具就是PDF latex。 35 | 36 | 这里我需要用到一个plot数据的PNG文件,这个文件将会被包含在我的文档中。 37 | 我想要表达的是,如果这两个依赖关系中的任意一个发生了变化,我希望生成paper PDF文件。 38 | 这两个文件都需要存在,如果它们发生了变化,我希望重新生成。 39 | 但我并没有告诉它如何生成这个plot数据的PNG文件,因此我可能还需要一个规则。 40 | 因此,我在这里定义了另一个目标,它的样子是这样的:plot - % & %代表任意字符串,可以理解为通配符模式。 41 | 但是,很酷的一点是,当你在依赖关系中重复这个模式时,我可以说,plot - % dot PNG将依赖于% dot data或debt,这是一种常见的数据文件后缀。 42 | 它还将依赖于一些脚本,这个脚本将实际为我绘制这个图形,而从一个文件格式到另一个文件格式的规则可以是多行的,但在我这个特定的例子中,只有一行。 43 | 我会在接下来的时候解释这是什么意思。 44 | 好的,所以在这里,我们要说的是,为了从通配符点点文件(与目标中的通配符匹配)和plot dot python文件转换,运行python文件,并且使用-i参数,这类似于我们在python文件中标记输入的方式。 45 | 稍后我会向您展示它。 46 | $*是一个为您定义的特殊变量,可以匹配任何百分号。 47 | 因此,如果我做plot to PNG,那么它将查找foo.dot,它的星号可以扩展为foo。 48 | 所以,这将产生与我们在此处匹配的文件名相同的文件名,而dollar act是一个特殊变量,表示目标的名称,对吧?所以,输出文件,希望plotter py会获取这里的任何数据,它将以某种方式生成PNG文件,并将其写入由dollar all指示的文件中,对吧?现在我们有了一个make文件。 49 | 让我们看看如果这个目录中唯一的文件是make文件,而我们运行make会发生什么。 50 | 它会说:“no rule to make target paper dot tex needed by paper dot PDF,stop。 51 | ”所以,它在这里所表达的是,首先,它查看了我们文件的第一条规则,也就是第一个目标,当你没有给make任何参数时,它会尝试构建第一个目标。 52 | 这被称为默认目标。 53 | 因此,在这种情况下,它试图为我们有用地构建paper dot PDF文件,然后查找它的依赖关系,它说:“为了构建paper dot PDF,我需要paper Tex和这个PNG文件。 54 | 55 | 我找不到paper.X的文件,也没有生成paper.X的规则,因此我要退出。 56 | 我无能为力了。 57 | 所以,让我们尝试在这里创建一些文件。 58 | 我们可以创建一个空的paper.X文件,然后输入make命令。 59 | 现在它会显示“paper to PDF”需要plot data.PNG文件,但是没有相关规则。 60 | 现在它知道它有一个依赖项,但它不知道如何获取另一个依赖项。 61 | 它知道有一个与之匹配的目标,但实际上找不到依赖项,所以它最终什么都没有做。 62 | 它仍然需要我们为PNG的输入生成这个PNG文件。 63 | 因此,让我们实际上在这些文件中添加一些有用的内容。 64 | 幸运的是,我之前从plot.da.py中获得了一个,所以很好。 65 | 那么,这个文本文件是文本的样子。 66 | 它不是很漂亮,但是我定义了一个空文档。 67 | 我将包含图形,这是包含图像文件的方式。 68 | 我将包含plot data.PNG,这当然是为什么我们希望paper.PDF的依赖项是PNG文件。 69 | plot.da.py也不是很有趣。 70 | 它只是导入了一堆库。 71 | 它解析了-ion-Oh参数。 72 | 它从I参数中加载数据。 73 | 它使用了一个称为matplotlib的库,这对于快速绘制数据非常方便。 74 | 它将数据的第一列绘制为X轴,第二列绘制为Y轴。 75 | 因此,我们只需要一个每行都有两列X和Y的数据文件,然后将其保存为给定的-oh值的图像。 76 | 因此,我们需要一个数据文件,它将被命名为data.点,因为我们需要plot_data.dot.png,而我们的规则指定了从该模式到点文件的方式,即按照plot之后的内容来确定点文件的名称。 77 | 因此,如果我们需要plot-data,则我们需要data.dot that文件。 78 | 79 | 然后这个文件,我们只是要把它放入一些线性坐标,因为为什么不呢?那不是线性的。 80 | 好的。 81 | 现在,如果我们要做什么,嗯,好的,那么刚刚发生了什么?嗯,make首先用正确的文件运行plot.py生成PNG文件,然后它运行PDF latex paper.tex,我们下面看到的所有内容都是该工具的输出。 82 | 如果你想的话,我们可以消除这个工具的输出,这样我们就不必让它干扰我们的所有输出,但是通常情况下,你会注意到它运行了两个命令,然后以正确的顺序写入了随机值。 83 | 如果我们现在在当前目录下运行LS,我们会发现我们有一堆文件是由PDF latex生成的,但特别的是,我们有生成的PNG文件和paper.pdf。 84 | 如果我们打开paper.pdf文件,我们会看到它有一个图像,其中有一条直线,也许本身并不是一个非常令人惊讶或有趣的结果,但这真的非常方便,我可以做一些像如果我再次输入make,make只是说paper.pdf是最新的。 85 | 它不做任何工作。 86 | 每当你运行make时,它尝试以最小的工作量来产生你要求的任何东西。 87 | 在这种情况下,没有任何依赖项发生变化,因此没有重建paper或重建图。 88 | 现在假设我要编辑paper.tex,我要在这里添加hello,现在做make,然后如果我们向上滚动,我们会看到它没有再次运行plot.py,因为我不需要它。 89 | 没有依赖项发生变化,但它确实再次运行了PDF latex。 90 | 如果我们打开paper,分析hello在那里。 91 | 另一方面,如果我改变数据文件,然后把这个点改为每小时8美元,然后再做,那么现在它会重新绘制图形,因为数据发生了改变,并且它会重新生成PDF,因为图形发生了改变。 92 | 确实,这篇论文的结果是我们预期的。 93 | 所以这并不是说这个特定的管道非常有趣,因为它不是。 94 | 它只对非常非常简单的目标和规则有效。 95 | 但是当你开始构建更大的软件或可能存在依赖关系时,这会非常有用。 96 | 你甚至可以想象,如果你在写论文,你的一个目标可能是首先生成这个数据文件,对吧?因此,其中一个makefile目标可能是运行我的实验,对吧? 97 | 运行我的基准测试并将输出的数据点放入此文件中,然后绘制结果,一直到最终论文完成。 98 | 这样做的好处是,首先你不必记住运行所有命令的方式,也不必在任何地方写下来,工具会处理最少量的工作量。 99 | 通常情况下,你会发现有些事情也太麻烦了。 100 | 例如,还有子命令需要执行,如 make tests,它会编译整个软件并运行测试。 101 | 还可能有像 make release 这样的东西,它会打开优化并创建一个 tarball 并将其上传到某个地方。 102 | 因此,它会为你完成整个流程。 103 | 这样做的目的是减少构建过程中你必须做的工作量。 104 | 现在我们看到了一个非常简单的依赖示例,对吧?我们看到了你可以将文件声明为依赖项,但你也可以声明传递依赖项。 105 | 当你在更大的软件领域中处理依赖项时,通常会发现你的系统最终会有许多不同类型的依赖项。 106 | 其中一些是像我们在这里看到的文件,有些是程序,比如这个程序隐含依赖于 Python 在我的机器上安装。 107 | 有些可能是库,比如你可能依赖于像 matplotlib 这样的东西,我们在这里也依赖于它。 108 | 有些可能是系统库,如 OpenSSL 或 OpenSSH 或低级密码库,你不一定要声明所有这些依赖项。 109 | 通常情况下,有一些工具可以帮助你管理这些依赖项。 110 | 而且,这些你可能依赖的系统通常存储在所谓的存储库中。 111 | 存储库只是一组通常相关的事物,你可以安装它们。 112 | 这基本上就是存储库的全部内容,你可能已经熟悉其中的一些,对吧?存储库的一些示例包括 PyPI,这是一个众所周知的 Python 包存储库,RubyGems,这是 Ruby 的类似存储库,crates.io 是 Rust 的存储库,NPM 是 Node.js 的存储库,但其他东西也有存储库,对吧?例如,有加密密钥的存储库,如 Keybase,有系统安装包的存储库,如如果你在 Ubuntu 或 Debian 中使用 apt 工具,则正在与一个包存储库交互,人们上传他们编写的程序和库,以便你可以安装它们。 113 | 同样地,你可能会有完全开放的仓库,比如说 Ubuntu 仓库通常由 Ubuntu 开发人员提供,但是在 Arch Linux 中,可能会有一个叫做 Arch 用户仓库的地方,用户可以自己共享他们自己的库和软件包。 114 | 仓库往往要么是受管理的,要么完全开放的,你应该经常意识到这一点,因为如果你使用完全开放的仓库,你从中得到的安全保证可能会比从受控制的仓库中得到的少。 115 | 如果你开始使用仓库,你会注意到一个问题,那就是很多软件都有版本号。 116 | 我所说的版本,你可能已经在像浏览器这样的东西中看到过了,对吧?其中可能有一些像 Chrome 版本 64.0.2019.0324 这样的东西,这是一个版本号。 117 | 它可能有一个点,这是一种版本号。 118 | 但有时候,如果你开始使用 Photoshop 或其他工具,可能会有其他种类的版本号,比如 8.1.7,这些版本号通常是数字的,但并不总是这样。 119 | 有时候它们会在其中添加哈希值,例如为了引用 Git 提交。 120 | 121 | 你可能会问,为什么我们需要这些版本号?为什么在发布软件时添加版本号如此重要呢?主要原因是它让我们能够知道软件是否会出现故障。 122 | 想象一下,我依赖于Jose编写的一个库,而Jose一直在对他的库进行更改以使它更好。 123 | 他决定将库中公开的一个函数的名称更改。 124 | 我的软件突然停止工作,因为我的库调用了Jose库中的一个函数,但是该函数不再存在。 125 | 取决于人们安装的Jose库的版本,版本号有助于我指定我依赖于Jose库的哪个版本,并且必须有一些规则来规定在给定版本中Jose可以做什么。 126 | 如果他做了一个我不能再依赖的更改,他的版本必须以某种方式更改。 127 | 关于版本发布的规则,版本号如何更改等问题存在许多想法。 128 | 其中一些仅受时间约束。 129 | 例如,如果您看一下浏览器,它们通常具有类似于时间版本的版本号。 130 | 它们在最左侧有一个版本号,表示哪个发布版,然后有一个通常为零的增量号,最后有一个日期。 131 | 因此,这是2019年3月24日的某个版本64的Firefox。 132 | 如果他们发布针对安全漏洞的补丁或热修复,他们可能会增加日期但保持左侧版本不变。 133 | 人们对方案的具体内容有着强烈的意见,而你需要了解其他人使用的方案。 134 | 如果我不知道Jose用于更改版本的方案,也许我只能说,你必须运行Jose软件的817版本,否则我无法构建我的软件。 135 | 但这也是一个问题。 136 | 想象一下,Jose作为他的库的负责开发人员,他发现安全漏洞并修复了它,但它没有改变库的外部接口。 137 | 没有函数改变,没有类型改变。 138 | 然后我希望人们使用他的新版本来构建我的软件,恰好构建我的软件可以使用他的新版本,因为那个特定版本没有改变我所依赖的任何东西。 139 | 因此,对此的一种尝试解决方案是称为语义版本控制。 140 | 在语义版本控制中,我们为版本号中由点分隔的每个数字赋予特定的含义,并为不同数字的增加制定合同。 141 | 142 | 特别是在语义化版本控制中,我们称这个为主要版本,这个为次要版本,这个为补丁版本。 143 | 其规则如下:如果你对你的软件进行了完全向后兼容的更改,例如不添加任何东西,不删除任何东西,不在外部重命名任何东西,就像什么也没有改变一样,那么你只增加补丁号,不做其他更改。 144 | 通常,例如安全修复会增加补丁号。 145 | 如果你添加了库中的某些内容(我只是称其为库,因为通常库是这种情况下需要考虑的内容),则增加次要版本并将补丁设置为零。 146 | 在这种情况下,如果我们要进行一个次要发布,则下一个次要发布版本号将为 8-0。 147 | 我们这样做的原因是,我可能对 José 在 2.0 中添加的某个特性有依赖性,这意味着你不能使用 8-1-7 构建我的软件。 148 | 尽管如果你已经将其编写为 8-1-7,你可以在 2-0 上运行它。 149 | 反之则不是这样,因为它可能尚未添加。 150 | 最后,如果进行了向后不兼容的更改,则增加主要版本。 151 | 如果我的软件以前可以使用你所拥有的任何版本,然后你做出了一些改变,这意味着我的软件可能不再工作,例如删除或重命名函数,则增加主要版本并将次要和补丁设置为零。 152 | 因此,下一个主要版本将是 9-0-0。 153 | 综合在一起,这些使我们能够在设置我们的依赖项时做很好的事情。 154 | 特别是,如果我依赖于某个人的库的特定版本,而不是说它必须完全是这个版本,我真正想表达的是它必须是相同的主要版本,至少是相同的次要版本,并且补丁可以是任何内容。 155 | 这意味着,如果我依赖于 José 的软件,则仍在同一主要版本内的任何后续版本都可以。 156 | 请记住,这包括较早的版本,假设次要版本相同。 157 | 想象一下,你在某台装有 8.1.3 版本的旧计算机上。 158 | 理论上,我的软件也应该可以正常工作在 8.1.3 版本上。 159 | 它可能有 José 在其中修复的任何漏洞,例如任何安全问题。 160 | 但这具有很好的特性,现在你可以在计算机上的许多不同软件之间共享依赖关系。 161 | 162 | 如果您已经安装了8.3.0版本,并且有许多不同的软件需要8.1.7、8.2.4和8.0.1版本中的一个,那么它们都可以使用同一个依赖版本。 163 | 您只需要安装一次即可。 164 | 这种语义化版本控制中最常见或最熟悉的例子之一是Python版本控制。 165 | 你们中的许多人可能已经遇到过这个问题,Python 3和Python 2彼此不兼容。 166 | 它们不向后兼容。 167 | 如果你在Python 2中编写代码并尝试在Python 3中运行它,它可能不起作用。 168 | 有些情况下会起作用,但那更多的是偶然而非其他。 169 | Python实际上遵循语义化版本控制,至少大部分是这样。 170 | 如果你编写运行在Python 3.5上的软件,那么它也应该在3.6、3.7和3.8上运行。 171 | 它不一定会在Python 4上运行,虽然那可能还需要很长一段时间。 172 | 但如果你为Python 3.5编写代码,它可能不会在Python 3.4上运行。 173 | 因此,你会看到许多软件项目尽可能降低它们的版本要求。 174 | 如果你可以依赖主版本和次版本以及修补版本0.0,那么这是你可以拥有的最好的依赖关系,因为它完全自由地依赖于你正在依赖的主要版本的哪个版本。 175 | 有时这很难。 176 | 有时你确实需要添加一个特性,但你可以把版本要求降到最低,这对于那些想要依赖你的软件的人来说是最好的。 177 | 反过来,在处理这些依赖管理系统或一般版本控制时,你经常会遇到锁定文件的概念。 178 | 你可能已经看到过这个问题,当你尝试做某事时,它会说“无法协调版本”,或者你会收到像“锁定文件已经存在”这样的错误。 179 | 这些通常是有些不同的主题,但总的来说,锁定文件的概念是为了确保你不会意外地更新某些东西。 180 | 锁定文件本质上只是一个列出你依赖关系及其当前使用版本的列表。 181 | 我的版本字符串可能是8.1.7,而最新版本可能是3.0,但在我的系统上安装的版本不一定是其中的一个。 182 | 它可能是8.2.4或类似的版本,锁定文件会说“依赖项José版本8.2.4”。 183 | 你想要一个锁定文件的原因有很多。 184 | 其中一个原因是您可能希望构建速度快。 185 | 如果每次尝试构建项目时,您使用的任何工具都会下载最新版本,然后编译它,再编译您的项目,那么每次等待时间可能会很长,具体取决于依赖项的发布周期。 186 | 如果您使用锁文件,除非您已在锁文件中更新了版本,否则它将仅使用之前为该依赖项构建的内容,您的开发周期可以更快。 187 | 188 | 使用锁文件的另一个原因是为了获得可重复的构建。 189 | 假设我生产了一些与安全相关的软件,我非常仔细地审查了我的依赖关系,并生成了一个签名的二进制文件,例如“这是我的宣誓声明,证明这个版本是安全的”。 190 | 如果我没有包含锁文件,那么当其他人安装我的程序时,他们可能会得到一个更晚的依赖版本,而这个版本可能已被黑客攻击或存在其他安全漏洞,而我还没有机会查看。 191 | 锁文件基本上允许我冻结生态系统,因为它是我已检查的版本。 192 | 这种做法的极端版本是一种称为“vendoring”的东西。 193 | 当您对依赖项进行vendoring时,实际上就是将它们复制/粘贴到项目中。 194 | Vendoring意味着将您关心的任何依赖项复制到您的项目中,因为这样您就完全确定了将获得该依赖项的版本。 195 | 这也意味着您可以自己修改它,但它的缺点是您将不再获得版本控制的好处。 196 | 您将无法自动获取软件的新版本,例如当Hosea修复其安全问题时(当然他没有安全问题)。 197 | 您会注意到,当谈论这个问题时,我一直在谈论系统中的一些更大的过程。 198 | 这些过程包括测试,检查依赖关系版本以及设置构建系统等。 199 | 通常,您不只想要一个本地构建系统,您希望构建过程包括其他类型的系统,即使您的计算机不一定开着,它们也可以运行。 200 | 因此,随着您开始处理越来越大的项目,您会看到人们使用持续集成的概念,持续集成系统本质上是一个云构建系统。 201 | 这意味着您的项目存储在互联网的某个地方,并且您已经使用某种服务为您的项目运行持续集成,无论它是什么。 202 | 持续集成可以是各种各样的东西。 203 | 它可以是像将您的库自动发布到PyPI一样简单,每次提交到特定分支时。 204 | 它可以是类似于每当有人提交拉取请求时运行测试套件,或者可以是每次提交代码时检查您的代码风格。 205 | 您可以通过持续集成实现各种各样的事情,最简单的方法是将它们视为事件触发的操作。 206 | 因此,每当某个事件发生时,可能会针对您的项目发生特定的操作,其中操作通常是某种脚本、一系列要调用的程序,它们将执行某些操作。 207 | 这实际上是一个涵盖了许多不同类型的服务的大伞术语。 208 | 一些持续集成服务是非常通用的东西,比如Travis CI、Azure pipelines或GitHub actions都是非常广泛的CI平台。 209 | 它们旨在让您编写在您定义的任何事件发生时要发生的操作,非常广泛的系统。 210 | 还有一些更专业的系统,处理诸如持续集成覆盖测试之类的事情。 211 | 212 | 所以,像注释你的代码并展示你没有测试这段代码,它们仅用于这个目的,或者它们仅用于测试基于浏览器的库之类的东西。 213 | 因此,通常你可以找到为你正在工作的特定项目构建的CI工具,或者你可以使用其中的一个更广泛的提供者。 214 | 而且,其中一个好处是,许多CI工具实际上是免费的,特别是针对开源软件,或者如果你是学生,你也经常可以免费获得它们。 215 | 一般来说,你使用CI系统的方式是向你的代码库添加一个文件,这个文件通常称为“recipe”。 216 | 而这个recipe指定的内容是这种依赖循环,就像我们在make文件中看到的那样,但并不完全相同。 217 | 事件,而不是文件,可能是这样的,比如当有人推送提交时,当提交包含特定的消息时,或者当有人提交pull request或持续写入时。 218 | 一个不与你的代码的任何特定更改相关的持续集成服务的例子是Dependabot。 219 | 你可以在GitHub上找到它,而Dependabot是你连接到你的代码库中的一个东西,它将只扫描你没有使用的依赖项中是否有更新的版本。 220 | 因此,例如,如果我依赖于8.1.7,并且我有一个锁定文件将它锁定在8.2.4上,然后8.3.0发布了,Dependabot会说:“你应该更新你的锁定文件”,然后向你的代码库提交该更新的pull request。 221 | 这是一个持续集成服务。 222 | 它不是与我改变任何东西相关,而是与生态系统的整体变化相关。 223 | 这些CI系统通常也会与你的项目集成。 224 | 因此,这些CI服务经常会提供一些小徽章之类的东西。 225 | 让我举个例子。 226 | 例如,这是我最近参与的一个设置了持续集成的项目。 227 | 所以,这个项目,你会注意到它的README。 228 | 如果我可以用那个Chrome bean放大它,不,不,那比我想要的大得多。 229 | 在这里,你会看到在代码库页面的顶部有一堆这样的徽章,它们显示各种类型的信息。 230 | “你会注意到我正在运行Dependable,对吧?所以依赖项当前是最新的。 231 | 它告诉我主分支上的测试套件是否正在通过。 232 | 233 | 它告诉我代码中有多少被测试覆盖了,并告诉我这个库的最新版本和在线可用的文档版本。 234 | 所有这些都由各种持续集成服务管理。 235 | 另一个你们中有些人可能会发现有用甚至熟悉的例子是GitHub Pages。 236 | GitHub Pages是GitHub提供的一个非常好的服务,它允许你设置一个CI操作,将你的存储库构建为博客。 237 | 它运行一个静态站点生成器称为Jekyll,Jekyll只需要一堆Markdown文件,然后生成一个完整的网站。 238 | 作为GitHub Pages的一部分,它们还会将其上传到GitHub服务器并在特定域名下提供。 239 | 这实际上是课堂网站的工作方式。 240 | 课堂网站不是一堆我们管理的HTML页面。 241 | 相反,有一个名为'Missing Semester'的存储库。 242 | 如果你查看Missing Semester存储库,你会看到,如果我在这里缩小一下,它只有一堆Markdown文件。 243 | 这里有'Saket 20/20 - Metaprogramming.md',所以这是今天讲座的原始Markdown。 244 | 这是我编写讲座笔记的方式,然后我将其提交到我们的存储库,并将其推送。 245 | 每当发生推送时,GitHub Pages CI将运行GitHub Pages的构建脚本,并为我们的课程生成网站,而无需我执行任何其他步骤来完成这一过程。 246 | 所以,是的,抱歉,好的,是的。 247 | 所以Jekyll,它使用一个称为Jekyll的工具,它是一个将包含Markdown文件的目录结构转换成网站的工具。 248 | 它会生成像HTML文件这样的文件,然后作为操作的一部分,它会将这些文件上传到GitHub服务器的一个特定域名下,通常是他们控制的'github.io'域名下。 249 | 然后我将Missing Semester设置为指向GitHub域名。 250 | 我想给大家一个关于测试的旁白,因为这是你们中很多人以前可能熟悉的东西,对测试有一个大致的概念。 251 | 你会在之前运行测试。 252 | 你曾经看过测试失败。 253 | 你知道基础知识,或者你从来没有见过测试失败。 254 | 255 | 如果你已经掌握了这些知识,那么恭喜你,但是当你开始涉及更高级的项目时,你会发现人们对于测试有很多专业术语。 256 | "测试是一个非常深入的主题,你可能需要花费许多时间来理解其中的细节。 257 | 虽然我不会详细介绍所有的内容,但是有几个词汇我认为是有用的,你需要知道它们的含义。 258 | 第一个是测试套件。 259 | 测试套件是程序中所有测试的简单名称。 260 | 它只是一组测试,通常作为一个整体运行,通常由不同类型的测试组成。 261 | 第一个类型是单元测试。 262 | 单元测试是一种通常比较小的、自包含的测试,用于测试一个单一的功能。 263 | 什么是一个功能可能有点取决于项目,但是这个想法是应该有一个微型测试,只测试一个非常特定的东西。 264 | 然后你有更大的测试,称为集成测试。 265 | 集成测试尝试测试程序的不同子系统之间的交互。 266 | 所以这可能是这样一个例子:如果你正在编写一个 HTML 解析器,单元测试可能是测试它是否能够解析 HTML 标记。 267 | 而集成测试可能是这样一个例子:这是一个 HTML 文档,请解析它。 268 | 这将是解析器的多个子系统的集成。 269 | 你还有一个称为回归测试的概念。 270 | 回归测试是测试过去出现过问题的东西。 271 | 假设有人向你提交了一个问题,并说如果我给它一个跑马灯标记,你的库就会出现问题,这让你很难过,所以你想要修复它。 272 | 因此,你将修复解析器以支持我的标记,并在测试套件中添加一个检查是否可以解析跑马灯标记的测试。 273 | 这样做的原因是为了将来不会再次引入该错误。 274 | 因此,回归测试非常有用,随着时间的推移,你的项目将会积累越来越多这样的测试,它们非常好,因为它们可以防止项目退回到早期的 bug。 275 | 我想要提及的最后一个概念是模拟。 276 | 277 | "mocking"是指能够用一种虚拟的方式替换掉系统的某些部分,这种虚拟版本的行为是由你控制的。 278 | 一个常见的例子是,你正在编写一个通过SSH进行文件拷贝的工具。 279 | 在这里,你可能想要模拟许多东西。 280 | 例如,在运行测试套件时,你可能并不关心是否有网络存在。 281 | 所以,你可以模拟网络。 282 | 通常的做法是,在你的库中,有一些东西打开连接,从连接中读取数据,或者向连接中写入数据。 283 | 你会在你的库内部覆盖这些函数,使用你专门为测试而编写的函数。 284 | 例如,读取函数只会返回数据,写入函数只会将数据丢弃。 285 | 同样,你可以为SSH功能编写一个模拟函数。 286 | 你可以编写一个不实际进行加密和与网络通信的函数。 287 | 这个函数只是接收字节,然后在另一端神奇地将它们弹出来。 288 | 你可以忽略其中的一切,因为对于文件拷贝的目的,如果你只是想测试该功能,那么下面的内容对于该测试并不重要。 289 | 通常情况下,在任何一种语言中,都有工具可以很容易地构建这些模拟抽象。 290 | 这就是我想讨论元编程的全部内容,但这是一个非常广泛的主题。 291 | 像持续集成、构建系统这样的东西,有很多可以让你对你的项目做很多有趣的事情,所以我强烈建议你稍微了解一下。 292 | 这些练习内容比较散,但这是件好事。 293 | 它们旨在尝试向你展示构建和处理这些过程的各种可能性。 294 | 例如,最后一个练习让你自己编写一个持续集成操作,你可以决定事件和操作是什么,但要尝试真正地构建一个。 295 | 这可能是你在项目中会发现有用的东西。 296 | 我在练习中给出的示例是尝试构建一个操作,对你的存储库运行英语语言的语法检查,例如RightGood或Proselint。 297 | 如果你真的这样做了,我们可以在课堂存储库中启用它,这样我们的讲义就会写得很好。 298 | 这种持续集成测试的另一个好处是,你可以在项目之间进行协作。 299 | 如果你编写了一个,我可以在我的项目中使用它,你可以构建这种提高一切的生态系统。 300 | 301 | 今天录制的任何内容有任何问题吗?是的,问题是,为什么我们既有make又有CMake?它们都是做什么的?它们之间有交流的原因吗?所以,CMake,我实际上不知道CMake的标语是什么,但它有点像C语言的更好的make。 302 | 顾名思义,CMake通常比make文件更好地了解C项目的布局。 303 | 它们的建立是为了尝试解析您的依赖项结构是什么,从一个规则到另一个规则的转换。 304 | 它还与像系统库这样的东西集成得更好,因此CMake可以执行诸如检测计算机上的给定库是否可用,或者如果该库在多个不同的路径上可用,它会尝试找到其所在的路径,然后进行适当的链接。 305 | 因此,CMake比make更智能。 306 | make将只执行您在make文件中放置的任何内容。 307 | 这不是完全正确的,有称为隐式规则的东西,它们是make中的内置规则,但它们非常简单,而CMake则尝试成为一个大型构建系统,它默认针对C项目工作。 308 | 同样,还有一个名为Maven和Ant的工具,这是另一个项目。 309 | 它们都是为Java项目构建的。 310 | 它们了解Java代码如何相互交互,如何构造Java程序,并为此而构建。 311 | 通常,当我使用make时,我会在顶部使用make,然后使其他工具调用构建它们知道如何构建的任何子系统。 312 | 对我来说,make是我可能编写的顶部粘合剂。 313 | 通常,如果您的make文件变得非常大,有更好的工具。 314 | 您会发现在大公司,例如谷歌,他们通常有一个构建系统来管理其所有软件。 315 | 因此,如果您看看谷歌,他们有这个名为Basil的开源系统,我不认为谷歌在谷歌内部直接使用Basil,但它基于他们在内部使用的构建系统。 316 | Basil实际上是建立为一种多语言构建框架,这是我认为他们称之为的想法。 317 | 因此,这个想法适用于许多不同的语言。 318 | 有像这种语言和那种语言的Basil模块,但它们都与相同的Basil框架集成,然后该框架知道如何在不同库和不同语言之间集成依赖项。 319 | 你有问题吗?当你说表达式时,你是指这个文件中的东西吗?是的,所以make文件是它们自己的语言。 320 | 321 | 他们是。 322 | 这是一种相当奇怪的语言。 323 | 就像Bash一样,它有很多奇怪的例外。 324 | 在很多方面,它就像Bash一样奇怪,但在不同的方面更糟糕。 325 | 当你编写makefile时,你可能会认为自己正在编写Bash,但实际上它有不同的错误。 326 | 但是,它确实是一种独立的语言。 327 | makefile通常的结构是你有一系列的指令,我想他们称之为指令。 328 | 每个指令都有一个冒号,在冒号左侧的是一个目标,在冒号右侧的是一个依赖项。 329 | 然后,下面的所有行都是操作序列,也称为规则,用于一旦你有了依赖项,如何构建这些目标?注意,makefile非常注重你必须使用制表符来缩进规则。 330 | 如果不这样做,make就不会工作。 331 | 它们必须是制表符,不能是4或8个空格,必须是制表符。 332 | 你可以在这里有多个操作。 333 | 我可以做echo hello或其他什么,然后它们将首先运行这个,然后运行这个。 334 | 今天的讲座中有一个练习,让你尝试扩展这个makefile,加入一些其他有趣的目标,更详细地介绍一下。 335 | 还有一些能力来执行外部命令,以确定依赖项可能是什么,如果你的依赖项不是静态文件列表,但是它有一点限制。 336 | 通常,一旦你开始需要这种东西,你可能需要转移到更高级的构建系统。 337 | 是的,问题是,如果我有,假设我有库a和库B,它们都依赖于库C,但库a依赖于4.0.1,库B依赖于3.4.7,会发生什么? 338 | 所以它们都依赖于 C,理想情况下,我们希望重用 C,但它们依赖于不同的主要版本的 C。 339 | 我们该怎么办?这种情况下发生的情况完全取决于您正在使用的系统和语言。 340 | 在某些情况下,工具会选择其中一个版本,这暗示它们实际上没有使用语义化版本控制。 341 | 在某些情况下,工具会显示“不可能”。 342 | 如果这样做,会导致错误,并且工具将告诉您需要升级 B,例如使 B 使用 C 的较新版本,或者降级 A。 343 | 你不能这样做,编译将失败。 344 | 有些工具将构建两个版本的 C,然后在构建 A 时使用 C 的主要版本 4,在构建 B 时使用 C 的主要版本 3。 345 | 你最终会遇到一些非常奇怪的情况,例如如果 C 有依赖项,则现在必须将所有 C 的依赖项都构建两次,一次用于 3,一次用于 4,也许它们共享,也许它们不共享。 346 | 你可能会遇到特别奇怪的情况。 347 | 如果想象一下库 C,比如说库 C 写入一个文件,比如说写入磁盘上的一些缓存。 348 | 如果现在运行您的应用程序,A 做了一些调用,比如调用 C.dot save,而 B 做了一些调用,比如调用 C.adult load,那么底层的应用程序就不会工作,因为格式是不同的,对吧?因此,这些情况通常非常棘手,大多数支持语义化版本控制的工具会因为这个原因拒绝这种配置。 349 | 但是,要自己搞砸是很容易的。 350 | 好了,我们明天再见,进行安全方面的学习。 351 | 请记住,如果您还没有填写问卷调查,请务必参加。 352 | 在问卷调查中,我最关心的问题是您希望我们在最后两节课中涵盖什么内容。 353 | 因此,请选择您想要我们讨论的主题并提出任何问题。 354 | 如果可以,请填写问卷调查。 355 | 就这样,明天见。 356 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture11_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 我想我们应该先介绍一下这个问题,这是一个自由问答的讲座,你们两个可以问问题,但也包括在家里的所有人。 2 | 我们事先收到了一些问题,但你们也可以在讲座期间提出其他问题。 3 | 对于在场的两位,你们可以举手提问,也可以匿名地在论坛上提交问题,这取决于你们自己。 4 | 无论如何,我们将会回答一些已经被问到的问题,尽可能给出有用的答案,虽然我们没有做过任何准备。 5 | 好的,这就是我们的计划。 6 | 我们将按照问题的受欢迎程度来回答。 7 | 开始吧!第一个问题是:有没有关于学习操作系统相关主题,比如进程、虚拟内存、中断、内存管理等的建议?我认为这是一个有趣的问题,因为这些都是非常低层的概念,除非你必须在某些情况下处理这些问题,否则通常并不重要,对吧?比如你正在编写非常低层的代码,比如实现内核或者想要对Linux内核进行修改。 8 | 否则,你很少需要自己处理这些问题,尤其是像虚拟内存和中断这样的问题。 9 | 进程是一个更一般的概念,我们在这门课程中也稍微谈到了一些,并且工具如htop、pgrep、kill、信号等也是相关的。 10 | 11 | 关于学习这个问题,也许最好的方法之一是尝试参加这个主题的入门课程。 12 | 例如,麻省理工学院有一个称为6.828的课程,你可以根据给定的代码构建和开发自己的操作系统,所有实验和课程资源都是公开的。 13 | 通过亲自动手去实践,这是学习的好方法。 14 | 此外,还有各种在线教程,可以指导你从零开始编写内核。 15 | 这不一定是一个非常精细的内核,不能运行任何真正的软件,但它可以教你基本知识。 16 | 你可以搜索如何使用你选择的编程语言编写内核,你可能找不到可以在Python中编写内核的教程,但是在C、C++、Rust等语言中,有许多这样的教程。 17 | 关于操作系统的另一个注释,正如Jon提到的,麻省理工学院有一个6.828的课程,但如果你想了解更高层次的概述,不一定是关于编程或操作系统,而是学习概念,另一个好的资源是Andy Tannenbaum写的《现代操作系统》一书。 18 | 实际上还有一本名为《FreeBSD操作系统》的书也非常好。 19 | 它并没有涉及Linux,而是介绍了FreeBSD,BSD内核的组织和文档可能比Linux更好,因此它可能是比尝试理解Linux更温和的介绍一些主题的方式。 20 | 21 | 下一个问题,你会优先学习哪些工具?也许我们都可以分享一下我们的看法?好的。 22 | 首先优先学习哪些工具?我认为熟练掌握编辑器可以在各个方面为你提供帮助,例如高效地编辑文件,这基本上就是你会花费大部分时间做的事情。 23 | 而且总体来说,尽量使用键盘而不是鼠标,这意味着你可以花更多时间做有用的事情,而不是浪费时间在移动上。 24 | 我认为这应该是我的首要任务,所以我会说,优先学习哪些工具取决于你正在做什么。 25 | 我认为核心思想是要尝试找到你正在反复执行的任务类型,例如,如果你正在执行某种机器学习工作量,并且发现自己经常使用我们昨天介绍的Jupyter笔记本,那么使用鼠标可能不是最好的选择,你需要熟悉键盘快捷键。 26 | 几乎任何事情都是如此,你最终会发现有一些重复的任务,你正在运行一台计算机,而只是试图找出,“哦,可能有更好的方法来做这件事”,无论是终端还是编辑器。 27 | 学习使用我们涉及的一些主题可能非常有趣,但如果它们在日常工作中不是非常有用,那么可能不值得优先考虑它们。 28 | 29 | 在我看来,本课程所涵盖的主题中,我认为最有用的两个是版本控制和文本编辑器。 30 | 我认为它们有点不同,因为文本编辑器非常有用,但在我们开始使用Vim和它所有的花式键盘快捷键之前,你可能已经在使用其他文本编辑器,并且可以很好地编辑文本,只是可能有点低效。 31 | 而我认为版本控制是另一个非常有用的技能,如果你不真正了解该工具,它实际上可能会导致一些问题,例如数据丢失或与人无法有效合作。 32 | 因此,我认为版本控制是值得好好学习的第一件事。 33 | 是的,我同意这一点。 34 | 我认为学习像Git这样的工具将在以后为你节省很多心酸。 35 | 此外,它还有助于你与他人合作。 36 | 安尼什在上一堂课中提到了GitHub,学会使用这个工具可以在参与其他人正在进行的大型软件项目时帮助你。 37 | 这是一项非常宝贵的技能。 38 | 39 | 关于"何时使用Python, 何时使用Bash脚本或其他编程语言"这个问题,这很难回答,因为正如Jose之前所说的,这取决于你要做什么.对于我来说,特别是对于Bash脚本来说,它是用于自动运行一堆命令的.你不想在Bash脚本中编写其他的业务逻辑.比如说,'我想按这个顺序运行这些命令...也许带一些参数?' 但是,即使这样,一旦你开始传递参数,你也不确定是否需要一个Bash脚本.同样的,一旦你开始进行任何类型的文本处理或配置,就要使用比Bash更专业的编程语言. Bash只适用于短小的单次脚本或在终端、shell上有一个非常明确的使用案例. 对于稍微具体的指导方针,可以说,"如果代码行数不到一百行,可以编写一个Bash脚本",但一旦超过这个点,Bash就变得不灵活,并且可能值得转向一个更为专业的编程语言,比如Python.此外,我想补充的是,有时候我发现自己在Python中编写脚本,因为如果我已经在Python中解决了某些子问题,我发现使用之前在Python中找到的解决方案比尝试重复使用Bash代码更容易。 40 | 41 | 同样的,很多人写了像Python库或Ruby库这样的东西来完成很多这样的任务,这种方式是非常好的。 42 | 而在Bash中,很难进行代码重用。 43 | 事实上,如果你在某种语言中找到一个帮助你完成任务的库,通常应该使用该语言来处理这项任务。 44 | 在Bash中,没有库,只有计算机上的程序。 45 | Bash很难写对。 46 | 对于你现在要解决的特定用例,很容易写对,但是,例如“如果一个文件名中有一个空格怎么办?”这种问题在Bash脚本中会导致很多错误和问题。 47 | 如果使用真正的编程语言,那么这些问题就会消失。 48 | 是的!检查过了。 49 | 接下来,我们来回答一个问题,那就是“源脚本”和“执行脚本”的区别是什么?这个问题我们在一段时间前的办公时间也曾提到过,就是“它们不是一样的吗?它们不都只是运行Bash脚本吗?”的确,这两者都会执行脚本中的代码行。 50 | 51 | 它们之间的区别在于,"source"脚本是告诉你当前的Bash脚本会话来执行该程序,而另一个是"启动一个新的Bash实例,并在那里运行程序。 52 | "这对于像......想象一下,"script.sh"试图更改目录的情况是很重要的。 53 | 如果你像第二个调用"./script.sh"那样运行脚本,那么新的进程将更改目录。 54 | 但是,当脚本退出并返回到你的shell时,你的shell仍然保持在原地。 55 | 然而,如果你在脚本中执行"cd"并"source"它,你当前的Bash实例就会运行它,所以它会"cd"到你所在的地方。 56 | 这也是为什么,如果你定义函数,例如,你可能想在你的shell会话中执行它们,你需要"source"脚本而不是运行它,因为如果你运行它,那个函数将被定义在启动的Bash进程中,但它将不会在你当前的shell中被定义。 57 | 我认为这是它们之间最大的两个区别。 58 | 59 | 下一个问题是... "各种软件包和工具存储的位置在哪里,引用它们的方式是怎样的? /bin或/lib到底是什么?" 正如我们在第一次讲座中提到的那样,有一个名为PATH的环境变量,它是一个以分号分隔的字符串,其中包含您的Shell查找二进制文件的所有位置。 60 | 如果您只是执行"echo $PATH",您将获得此列表;所有这些位置将按顺序进行查询。 61 | 它将遍历所有位置,并且实际上——我们已经...我们介绍过哪些?+ 是的。 62 | 因此,如果您运行"which"和一个特定的命令,Shell实际上会告诉您它在哪里找到此(命令)。 63 | 除此之外,有一些公约,许多程序将安装其二进制文件,例如/usr/bin(或者至少包含符号链接)在/usr/bin中,以便您可以找到它们。 64 | 还有一个/usr/local/bin。 65 | 有一些特殊的目录,例如,/usr/sbin仅用于sudo用户,一些这些公约在不同的发行版之间略有不同。 66 | 我知道有些发行版例如在/opt下安装用户库。 67 | 68 | 是的,我认为可以再详细介绍一下 /bin,Anish 可以说一下其他的文件夹。 69 | 关于 /bin,有一些惯例,通常 /bin 用于系统基本工具,/usr/bin 用于用户程序,/usr/local/bin 用于用户编译的程序,所以那些你想让用户运行的程序都应该放在 /usr/bin 中,用户自己编译并安装在系统中的程序通常会放在 /usr/local/bin,但是这也因机器和发行版而异。 70 | 例如,在 Arch Linux 上,/bin 是指向 /usr/bin 的符号链接。 71 | 它们是一样的,正如 Jose 提到的,还有 /sbin 专门为需要以 root 权限运行的程序准备,这也因发行版而异,有些发行版甚至没有这个目录,在许多系统上,/usr/local/bin 可能不在你的 PATH 中,甚至不存在于你的系统中。 72 | 73 | 另一方面,在BSD中,/usr/local/bin通常使用得更加频繁。 74 | 我们到目前为止讨论的都是Linux中文件和文件夹的组织方式。 75 | Linux、BSD与macOS等平台之间存在一些差异。 76 | 如果你想要确切地知道某个位置的用途,可以查阅相关资料。 77 | 但需要记住的一些一般规律是:带有/bin的文件夹中包含可执行的二进制程序,带有/lib的文件夹中包含库文件,可供程序链接使用。 78 | 还有一些其他的有用信息,例如,许多系统都有一个/etc文件夹,其中包含配置文件;/home文件夹下的每个子文件夹都是一个用户的主文件夹,在Linux上,我的用户名或者Anish的用户名将与/home/anish这个主文件夹相对应。 79 | 还有一些其他的文件夹,例如/tmp通常是一个临时目录,在重启时会被清空(但并非总是如此,需要在系统上检查);/var通常保存着随时间而变化的文件,通常会是像软件包管理器的锁文件、日志文件以及用于跟踪进程ID的文件;/dev文件夹中包含着系统的设备,通常是对应于系统设备的特殊文件。 80 | 我们之前提到过/sys,Anish提到了/etc。 81 | /opt是常见的第三方软件文件夹,一般用于公司将其软件移植到Linux上,但它们实际上并不理解在Linux上运行软件的情况,因此它们只是将所有东西放在一个目录中,并将其安装到/opt中。 82 | 我想这些是我能想到的所有文件夹了。 83 | 我们会在本次讲座后制作的讲座笔记中列出这些文件夹。 84 | 85 | 下一个问题:我应该使用 apt-get 安装 Python 包还是使用 pip 安装?这是一个很好的问题,我认为从更高的层面来看,这个问题正在询问我是否应该使用系统的软件包管理器来安装东西,还是应该使用一些其他的软件包管理器?比如在这种情况下,更特定于某个语言的软件包管理器。 86 | 答案也有点因情况而异。 87 | 有时使用系统的软件包管理器来管理东西很方便,因为所有的东西都可以在一个地方安装和升级,但我认为通常系统库中提供的东西,比如通过 apt-get 或类似工具获得的东西,可能会比更特定于某个语言的库稍微过时一些。 88 | 例如,我使用的很多 Python 包,我真的想要最新的版本,因此我使用 pip 来安装它们。 89 | 进一步说,有时候系统软件包可能需要一些你可能没有意识到的其他依赖项,而且对于某些系统,至少对于像 Alpine Linux 这样的系统,它们没有很多 Python 包的 wheel,所以编译它们会花费更长的时间。 90 | 它们需要从头开始编译,所以需要更多的空间。 91 | 而如果你只是使用 pip,pip 有许多不同平台的二进制文件,那可能会更好用。 92 | 93 | 另外你也应该知道,pip 在不同的电脑上可能不会做完全相同的事情。 94 | 比如,如果你在运行 x86 或 x86_64 的笔记本电脑或台式机上,你可能有二进制文件,但如果你在运行 Raspberry Pi 或其他嵌入式设备上,这些设备运行在不同的硬件架构上,你可能没有二进制文件。 95 | 我认为这也值得考虑。 96 | 在这种情况下,使用系统包可能是值得的,因为它们需要的时间比从头开始编译整个 Python 安装要短得多。 97 | 除此之外,我想不出任何例外情况,我会真正使用系统包而不是 Python 提供的包。 98 | 另外要记住的一件事是,有时你的电脑上会有多个程序,你可能正在开发多个程序,但由于某种原因,并不总是使用最新版本的东西来构建所有程序,有时它们会滞后一些。 99 | 当你在整个系统上安装某些东西时,你只能......这取决于你的具体系统,但通常只有一个版本。 100 | pip 可以让你做的,尤其是与像 python 的 virtualenv 这样的东西结合使用时,其他语言也有类似的概念,你可以说我想在它自己的子目录中编译这个包的依赖项,它所需的所有版本都将在那里构建,你可以为不同的项目单独做这个,所以它们有不同的依赖项或相同的依赖项,但不同版本仍然被分开保留。 101 | 这是使用系统包难以实现的一件事。 102 | 103 | 下一个问题是:什么是最容易和最好的性能分析工具,用于优化我的代码?这是一个我们可以讨论很长时间的话题。 104 | 最容易和最好的方法是使用时间打印输出。 105 | 就像,我不是在开玩笑,很多时候最简单的方法就在你的代码里。 106 | 在代码的顶部,你确定当前时间,然后在你的程序中进行二分查找,添加一个打印语句,打印自程序启动以来经过了多长时间,然后一直重复这样的步骤,直到找到花费时间最长的代码段。 107 | 然后进入那个函数,再次执行同样的操作,一直重复,直到找到大概的时间花费位置。 108 | 这并不是百分之百可靠的,但它非常简单,可以很快地为你提供有用的信息。 109 | 如果你需要更高级的信息,Valgrind有一个叫做cache-grind或call grind的工具。 110 | 这个工具可以让你运行你的程序并测量所有的调用堆栈,比如哪个函数调用了哪个函数,最终你会得到一个非常漂亮的程序源代码注释,基本上显示了每行花费的时间。 111 | 这会让你的程序变得慢十倍甚至更多,而且它并不支持线程,但是如果你能使用它,它非常有用。 112 | 如果你不能使用这些工具,那么像perf或其他语言的类似工具通常会提供一些采样分析类的有用数据,但是会有很多数据,它们会有一些偏见,并且通常会突出显示一些问题,有时很难提取有意义的信息来回答“我应该改变什么”。 113 | 相比之下,使用打印输出的方法可以很快地让你知道哪些代码部分是糟糕的或缓慢的。 114 | 火焰图也很好用,可以用来可视化一些信息。 115 | 是的,我只有一件事要补充,通常编程语言都有专门的工具用于分析性能,所以要找出适合你的语言的正确工具,比如如果你在Web浏览器中使用JavaScript,Web浏览器有一个非常好用的分析工具,你应该使用它。 116 | 或者如果你正在使用Go,例如,Go有一个内置的性能分析工具,非常好用,你应该使用它。 117 | 最后要补充的是,有时你可能会发现自己正在进行这种时间上的二分查找,你找到的时间可能是由于你正在等待网络或文件等原因导致的,如果是这种情况,你需要确保所花费的时间是最小的,比如如果我想要写一个1GB的文件或读取一个1GB的文件并将其存入内存,你需要检查实际的时间是否是你实际必须等待的最小时间。 118 | 如果时间是十倍长,你应该尝试使用我们在调试和性能分析部分中介绍的其他工具,以查看为什么你没有利用所有的资源,因为这可能是发生的很多事情,例如在我的机器学习工作负载研究中,大部分时间都是用来加载数据的,你必须确保加载数据的时间实际上是你想要的最短时间。 119 | 并且在此基础上,实际上有专门的工具可以进行等待时间分析。 120 | 很多时候,当你在等待某些东西时,真正发生的事情是你正在发出系统调用,这个系统调用需要一些时间来响应。 121 | 122 | 如果你需要进行大量的写入或读取操作,或者需要进行多个此类操作,那么一件非常方便的事情是尝试从内核中获取关于程序花费时间的信息。 123 | 现在有一个相对较新的可用工具,叫做 BPF 或 eBPF,它本质上是内核跟踪工具,可以进行一些非常酷的操作,包括跟踪用户程序。 124 | 虽然它可能有点棘手,但是如果你需要进行这种低级性能调试,我建议你看看一个叫做 BPF trace 的工具。 125 | 但是对于这种工作来说它真的非常好用,可以得到一些关于特定系统调用花费时间的直方图等信息。 126 | 这是一个非常好的工具。 127 | 你使用哪些浏览器插件?我尽量少使用插件,因为我不喜欢我的浏览器中有太多东西,但是有一些是比较常用的。 128 | 第一个是 uBlock Origin。 129 | uBlock Origin 是众多广告拦截器之一,但它不仅仅是一个广告拦截器。 130 | 它是一个网络过滤工具,可以让你做更多的事情,不仅仅是拦截广告。 131 | 它还可以像阻止某些域名的连接、阻止某些类型的资源的连接。 132 | 我把它设置成了高级模式,基本上可以禁用所有的网络请求。 133 | 但不仅仅是网络请求,我也禁用了每个页面上的所有内嵌脚本、所有第三方图像和资源,然后你可以为每个页面创建一个白名单,它会提供关于如何提高浏览安全性的非常低级别的工具。 134 | 但是你也可以将其设置为非高级模式,然后它会做很多与普通广告拦截器类似的事情,如果你在寻找广告拦截器,它可能是你想要使用的,并且它适用于几乎所有的浏览器。 135 | 我认为这是我的首选。 136 | 137 | 我觉得我最常使用的插件可能是一个叫做Stylus的插件。 138 | 它让你修改网页的CSS或样式表。 139 | 这非常方便,因为有时你在浏览一个网站时,想要隐藏一些你不关心的部分,比如广告或者某个侧边栏。 140 | 问题是,这些东西最终都会在你的浏览器中显示,而你可以控制哪些代码执行。 141 | 就像Jon所说的那样,你可以无限制地自定义它,我已经为很多网页设定了隐藏这一部分,或者为它们尝试制作暗黑模式,你几乎可以为每个网站更改颜色。 142 | 实际上,还有一个在线仓库,里面有很多人为网站编写的样式表。 143 | 所以,可能有人已经为GitHub制作了暗黑版,这使得浏览更加愉悦。 144 | 除此之外,还有一个并不很花哨,但我发现非常有用的插件,它可以截取整个网站的屏幕截图。 145 | 它会自动滚动并制作出整个网站的复合图像,这对于打印一个看起来很糟糕的网站非常有用。 146 | (它内置在Firefox中) 哦,有趣!既然你提到了Firefox内置的插件,还有一个我非常喜欢的是Firefox的多账户容器。 147 | (哦,是的,它非常棒!) 148 | 默认情况下,许多网页浏览器,比如Chrome,有一个叫做“会话”的概念,你在其中保存了所有的cookies,它们在不同的网站之间共享,也就是说,你打开新标签页时,除非你进入无痕模式,否则你使用的都是同一个档案。 149 | 而这个档案是适用于所有网站的,是通过一款浏览器插件还是内置功能实现的呢?(这是混合的,很复杂。 150 | )我想你实际上需要说出你想要安装它或启用它,而这个名字叫做“多帐户容器”,它可以让你告诉Firefox要有单独的隔离会话。 151 | 比如说,你想对每次访问Google或每次访问Amazon建立独立的会话,这样你就可以在浏览器级别上确保它们之间没有信息共享。 152 | 这比打开无痕窗口更方便,因为后者每次都会清除所有东西。 153 | (提醒一下,Stylus与Stylish之间的区别。 154 | )哦,是啊,我忘了这一点。 155 | 有一件重要的事情是浏览器扩展程序可以加载CSS样式表,它叫做Stylus,不同于旧版的Stylish,因为Stylish曾经被某个不怎么靠谱的公司收购,他们不仅使用这个功能,还读取了你的整个浏览器历史记录并将其发送回他们的服务器,以便进行数据挖掘。 156 | 于是人们就开发了这个开源的替代品,叫做Stylus,这是我们推荐的。 157 | 话虽如此,我认为两个工具的样式库是相同的,但我需要再次确认一下。 158 | 159 | "Anish,你有使用任何浏览器插件吗?" 160 | 161 | "是的,我也有一些浏览器插件的推荐。 162 | 我也使用uBlock Origin和Stylus,但我还要推荐一款密码管理器的集成插件。 163 | 我们在安全讲座的讲义中有涉及到这个主题,但我们没有详细讨论。 164 | 基本上,密码管理器在处理在线账户时可以大大提高您的安全性,与浏览器集成可以节省您很多时间。 165 | 您可以打开一个网站,然后它可以自动填充您的登录信息,而不是您必须在独立的程序之间来回复制和粘贴。 166 | 而且,这种集成可以避免某些攻击,否则如果您进行手动复制和粘贴,则可能会受到攻击,例如钓鱼攻击。 167 | 因此,如果您找到一个看起来非常类似于Facebook的网站,并使用Facebook的登录凭据进行登录,然后将正确的凭据复制粘贴到这个奇怪的网站,那么现在它就有了您的密码。 168 | 但如果您有浏览器集成,则扩展程序可以自动检查,例如:我是在F A C E B O O K.com上还是在其他看起来相似的域名上,并且如果是错误的域名,则不会输入登录信息。 169 | 所以,密码管理的浏览器插件很好用。 " 170 | 171 | "是的,我同意。 " 172 | 下一个问题是,还有哪些有用的数据整理工具?昨天的讲座中,我提到了curl。 173 | Curl是一个非常好用的工具,可以直接在终端中进行网页请求并将其转储。 174 | 您还可以使用它来上传文件,非常方便。 175 | 在那个讲座的练习中,我们还谈到了JQ和pup,这是命令行工具,可以让您在JSON和HTML文档上编写查询,非常有用。 176 | 其他的数据整理工具?啊,Perl。 177 | Perl编程语言通常被称为只能写不能读的编程语言,因为即使您编写它,也无法阅读它。 178 | 但是,它非常擅长于处理纯文本,没有什么能超越它。 179 | 因此,也许值得学习一些非常基础的Perl知识,以编写一些脚本。 180 | 这通常比编写一些grep、awk和sed的组合要容易得多,而且直接编写比例如Python的代码也要快得多。 181 | 但是除此之外,其他的数据整理工具呢?我脑海中没有想到。 182 | 实际上,"column -t"就非常好用。 183 | 如果您将任何以空格为分隔符的输入导入"column -t",它会使列的所有空格对齐,从而使您得到漂亮的对齐列。 184 | 这是一种非常实用的工具,还有"head"和"tail",但我们已经谈论过这些了。 185 | 我认为还有几个我经常使用的工具需要补充:一个是Vim。 186 | Vim在数据整理中非常有用。 187 | 有时候您可能会发现要完成的操作很难用管道运算符来表达。 188 | 但是,如果您可以打开文件,只需记录一些快速的Vim宏来执行所需的操作,那么这可能会更加容易。 189 | 这是其中一个工具,另一个是如果您正在处理表格数据,并且想要执行更复杂的操作,例如按一列排序,然后分组并计算某种统计数据,我认为这种工作负载的大部分内容都可以使用Python和pandas来完成,因为它专门为此而建立。 190 | 我还发现自己经常使用的一个相当不错的功能是它可以导出许多不同的格式。 191 | 因此,这种中间状态具有自己的pandas数据框架对象,但它可以导出到HTML、LaTeX等许多不同的表格格式。 192 | 如果你的最终产品是某种摘要表格,那么我认为Pandas是一个非常好的选择。 193 | 我也同意使用Vim和Python。 194 | 我认为这两个工具是我最常用的数据处理工具之一。 195 | 关于Vim,去年我们在系列课程的讲义中进行了演示,但是我们没有在课堂上介绍它。 196 | 我们进行了一个演示,将一个XML文件转换为同一数据的JSON版本,只使用了Vim宏。 197 | 我认为这实际上是我实践中的处理方式。 198 | 我不想去找一个工具来做这个转换。 199 | 将其编码成Vim宏实际上很简单,然后我就可以这样做。 200 | 此外,特别是在像Jupyter笔记本这样的交互式工具中,Python是进行数据处理的一种非常好的方式。 201 | 我还想提到第三个工具,我不记得我们是否在数据处理讲座或其他地方介绍过,那就是一个叫做pandoc的工具,它可以在不同的文本文档格式之间进行转换。 202 | 因此,你可以将纯文本转换为HTML或HTML转换为Markdown或LaTeX转换为HTML或许多其他格式。 203 | 它实际上支持大量的输入格式和输出格式。 204 | 我想最后还有一个,我在数据处理讲座中简要提到过,那就是R编程语言。 205 | 它是一个非常糟糕的(我认为是非常糟糕的)编程语言,我不会在数据处理流水线的中间使用它。 206 | 但是,在最后阶段,为了生成漂亮的图表和统计数据,R是非常好的。 207 | 208 | 因为 R 是为统计和绘图而建的,所以有一个叫做 ggplot 的库,非常棒。 209 | ggplot2 从技术上讲很棒。 210 | 它可以产生非常好的可视化效果,让你可以很容易地做出像数据集具有多个方面这样的东西,不仅仅是 X 和 Y,还有 Z 和其他变量,然后你想一次性将所有这些参数分组的吞吐量绘制成可视化效果。 211 | R 可以让你非常容易地做到这一点,我还没有看到其他工具能像 R 那样容易地做到这一点。 212 | 下一个问题,Docker 和虚拟机的区别是什么?最容易解释的方法是什么?所以,Docker 开始了一些叫做容器的东西,Docker 并不是唯一启动容器的程序。 213 | 还有许多其他的程序,通常它们依赖于底层内核的某些特性。 214 | 在 Docker 的情况下,它们使用了一些叫做 LXC 的东西,这是 Linux 容器。 215 | 基本原理是,如果你想启动一个看起来像虚拟机的东西,它运行的操作系统与你已经在计算机上运行的操作系统大致相同,那么你实际上不需要运行另一个内核实例。 216 | 实际上,另一个虚拟机可以共享一个内核,你可以使用内核内置的隔离机制来启动一个程序,让它认为它在它的硬件上运行,但实际上,它是在共享内核。 217 | 这意味着容器通常可以比完整虚拟机使用更低的开销运行。 218 | 但是你应该记住,它的隔离性也比较弱,因为你在两个容器之间共享内核。 219 | 如果你启动了一个虚拟机,唯一共享的是硬件和某种程度上的 hypervisor,而 Docker 容器则共享整个内核,这是一个不同的威胁模型,你可能需要考虑这一点。 220 | 221 | 正如Jon指出的那样,要使用容器(例如Docker),你需要底层操作系统与运行在容器顶部的程序所期望的操作系统大致相同。 222 | 如果你使用的是macOS,那么你需要在虚拟机中运行Linux,然后在Linux上运行Docker。 223 | 因此,如果你使用容器来提高性能,那么你就是在为性能而牺牲隔离性。 224 | 如果你在macOS上运行,那么可能无法达到预期的效果。 225 | 最后需要注意的是,Docker和容器有一个细微的区别。 226 | 使用容器需要注意的问题之一是,容器更类似于虚拟机,因为它们会持久化存储所有内容,而默认情况下,Docker并不会这样做。 227 | Docker的主要想法是“我想运行一些软件,我获得了镜像,然后它运行起来了”,如果你想要与主机系统链接的任何持久性存储,你必须手动指定,而虚拟机则使用提供的虚拟磁盘。 228 | 229 | 下一个问题是,每个操作系统的优点是什么,我们如何在它们之间进行选择?例如,选择最适合我们需求的Linux发行版。 230 | 我想说的是,对于很多任务来说,你所运行的具体的Linux发行版并不是那么重要的。 231 | 重要的是了解不同类型或者分组的发行版。 232 | 例如,有一些发行版有着很频繁的更新,但是它们更容易出问题。 233 | 例如,Arch Linux通过滚动更新的方式来推送更新,可能会出现一些问题,但是他们习惯于事情是这样的。 234 | 但是,如果你有一些非常重要的Web服务器来托管你所有的业务分析,你肯定希望它更新的方式更加稳定。 235 | 这就是为什么你会看到像Debian这样的发行版在他们推送更新时更加保守,或者比如Ubuntu区分了长期支持版本,它们只每两年更新一次,还有更加定期的更新,每年会有两次。 236 | 所以,了解到这种差别很重要。 237 | 此外,一些发行版提供二进制文件的方式以及仓库的方式也有所不同。 238 | 例如,我认为很多Red Hat Linux不想在官方仓库中提供非自由驱动程序,但Ubuntu则对其中的一些驱动程序没有问题。 239 | 除此之外,我认为大多数Linux发行版的核心是共享的,在共同点上有很多学习的内容。 240 | 所以,你不必担心具体的细节。 241 | 242 | 继续保持这个课程有些主观的主题,我想说如果你是第一次使用Linux,选择类似Ubuntu或Debian这样的东西。 243 | 所以,Ubuntu也是基于Debian的发行版,但可能更加友好。 244 | Debian则更加简约。 245 | 例如,我在所有的服务器上使用Debian,我在运行Linux的桌面计算机上使用Debian桌面。 246 | 如果你想尝试学习更多的东西,并且想要一种在稳定性和软件更加更新之间做出权衡的发行版,也许你可以考虑像Arch Linux、Gentoo或Slackware这样的东西。 247 | 噢,我想说,如果你正在安装Linux并想要完成工作,Debian是一个很好的选择。 248 | 是的,我同意这个观点。 249 | 另一个观察是,你可以安装BSD。 250 | BSD从过去到现在发生了很大的变化。 251 | 仍然有一些软件在BSD上无法使用,但它提供了一个非常完善的文档体验。 252 | 与Linux不同的是,在安装BSD时,你会得到一个完整的操作系统。 253 | 因此,许多程序由同一个团队维护,他们同时升级,这与Linux世界的工作方式有些不同。 254 | 这确实意味着事情通常会慢一点。 255 | 我不会将其用于游戏等方面,因为驱动程序支持一般。 256 | 但这是一个有趣的环境值得一看。 257 | 258 | 接下来,对于像 Mac OS 和 Windows 这样的东西,如果你是一个程序员,我不知道为什么你会使用 Windows,除非你正在构建 Windows 的东西,或者你想玩游戏之类的东西,但在这种情况下,也许尝试双重启动,即使这也很麻烦。 259 | Mac OS 是两者之间的一个很好的中间点,你可以得到一个相对不错的系统。 260 | 但你仍然可以在某种程度上访问一些较低级别的内容。 261 | 而且双重启动 Mac OS 和 Windows 也非常容易。 262 | 但 Mac OS 和 Linux,以及 Linux 和 Windows 之间就不是这种情况了。 263 | 264 | 265 | 好的,对于其余的问题,这些问题都没有人点赞,所以我们可以在课程的最后五分钟内快速回答它们。 266 | 下一个问题是 Vim 和 Emacs 呢?Vim!很容易的答案,但更严肃的回答是,我认为我们三个人都将 vim 作为我们的主要编辑器。 267 | 我在某些需要使用 Emacs 的研究特定工作中使用 Emacs,但在更高的层面上,两个编辑器都有有趣的思想。 268 | 如果你有时间的话,值得探索一下两者,看哪一个更适合你。 269 | 另外,你可以使用 Emacs 并在 vim 模拟模式下运行它。 270 | 我实际上认识很多这样做的人,这样他们就可以访问一些很酷的 Emacs 功能和一些酷的哲学思想。 271 | 就像 Emacs 可以通过 Lisp 进行编程一样,这很酷。 272 | 比 vimscript 好多了,但人们喜欢 vim 的模态编辑,所以有一个 Emacs 插件叫做 evil 模式,它在 Emacs 中提供了 vim 模态编辑。 273 | 所以这不一定是一个二元的选择,如果你愿意,可以将两个工具结合起来。 274 | 如果你有时间,值得探索一下两者。 275 | 276 | 下一个问题:机器学习应用有什么技巧或诀窍吗?我认为,了解这些工具的使用方法,尤其是数据处理方面的许多shell工具非常重要,因为作为机器学习研究人员,你似乎正在尝试不同的方法。 277 | 但我认为做到这一点的一个核心方面,就像许多科学工作一样,是能够以合理的方式获得可重复的结果并记录下来。 278 | 例如,与其想出如何命名你的文件夹以理解实验,也许直接使用一个描述整个实验的JSON文件更值得,我通常会在其中记录所有的参数,然后我可以使用我们已经介绍过的工具,快速查询使用特定数据集的实验。 279 | 除此之外,另一方面,如果你正在运行训练机器学习应用的任务,而你还没有使用类似于大学或公司提供的某种集群,而是像许多实验室一样手动ssh连接,这可能是因为这是一种简单的方式。 280 | 那么自动化这项工作是很值得的,因为手动进行这些操作可能看起来很不起眼,但却需要大量的时间和精力。 281 | 282 | 还有其他的vim技巧吗?我有一个建议。 283 | 在vim讲座中,我们尽量不会向您介绍太多不同的vim插件,因为我们不希望那堂课让您感到不知所措。 284 | 但我认为探索vim插件是值得的,因为有很多非常酷的插件可供选择。 285 | 一个资源可以使用不同的讲师点文件。 286 | 像我们大多数人一样,我使用了大约两打vim插件,我发现其中很多都很有用,而且我每天都在使用它们。 287 | 我们所有人都使用略微不同的子集。 288 | 因此,去看看我们使用了什么或查看我们链接到的其他资源,您可能会发现一些有用的东西。 289 | 另外,我认为我们在讲座中没有详细介绍过的是熟悉leader键,这是一种特殊的键,很多程序,特别是插件,都会链接到它,而且vim有很多常见操作的简短方式,但是你可以想出更快的方法来完成它们。 290 | 例如,我知道您可以使用分号WQ来保存和退出,或者可以使用大写ZZ,但我实际上只是使用leader(对我来说是空格),然后W。 291 | 我已经为我经常执行的很多常见操作做到了这一点。 292 | 因为在极其常见的操作中节省一个按键只是每月节省数千个按键。 293 | 294 | 是的,稍微展开一下什么是leader key,所以在vim中,您可以绑定一些键。 295 | 我可以做像Ctrl J这样的事情,就像按住一个键然后按另一个键。 296 | 我可以将其绑定到某个动作,或者我可以将单个击键绑定到某个动作。 297 | leader key让您做的是绑定。 298 | 因此,您可以分配任何键作为leader key,然后将leader加上其他某个键分配给某个操作。 299 | 例如,像Jose的leader key是空格,他们可以将空格与释放空格后的某个其他键组合在一起,绑定到任意vim命令。 300 | 它只是给您另一种绑定整套键组合的方法。 301 | Leader key加上键盘上的任何键可以实现某种功能。 302 | 我忘记我们是否在vim uh中涵盖了宏,但是学习vim宏是值得的。 303 | 它们并不复杂,但知道它们的存在以及如何使用它们将节省您很多时间。 304 | 另一个是称为标记的东西。 305 | 因此,在vim中,您可以按m,然后按键盘上的任何字母,以在该文件中打标记,然后可以按相同字母的单引号跳回同一位置。 306 | 例如,如果您在两个代码不同的部分之间来回移动,这将非常有用。 307 | 您可以将一个标记为A,另一个标记为B,然后使用tick A和tick B在它们之间跳转。 308 | 还有Ctrl + O,它跳转到文件中您上次在哪个位置,无论您因何而动。 309 | 例如,如果我在某行,然后跳转到B,然后跳转到A,Ctrl + O将带我回到B,然后回到我最初的位置。 310 | 如果您正在执行搜索,则可以使用Ctrl + O从搜索的位置移动到文件的其他部分。 311 | Ctrl + O还可用于在不同文件之间移动,因此如果我从一个文件转到另一个文件的某个位置,然后再返回第一个文件的某个位置,Ctrl + O将通过该堆栈将我移动回去。 312 | Ctrl + I可以向前移动该堆栈。 313 | 因此,它并不是一旦弹出就永远消失。 314 | 315 | "colon earlier" 这个命令真的很方便。 316 | 使用"colon earlier"可以获取文件的早期版本,这是基于时间而不是基于操作的。 317 | 例如,如果你按了一些撤销和重做操作并进行了一些更改,"earlier"将获取文件的真正早期版本并将其还原到缓冲区。 318 | 有时这很有用,如果你撤销了一些更改并重新写了一些内容,然后意识到你实际上想要撤销之前的版本,"earlier"可以让你这样做。 319 | 另外,还有一个叫做"undo tree"的插件,它允许你浏览vim保留的撤销历史记录的完整树形结构。 320 | 因为它不仅仅保留线性历史记录,而是保留了完整的树形结构。 321 | 探索这个功能在某些情况下可能会帮助你避免重新输入过去输入过的东西,或者你已经忘记了曾经使用的工作了的东西。 322 | 还有一个我想要提到的就是,我们提到过vim中有动词和名词,对于你的动词比如删除或复制,你还有名词,比如下一个字符或百分号来交换括号等等。 323 | 搜索命令是一个名词,所以你可以做一些类似D/某个字符串的操作,它会删除到下一个匹配的字符串。 324 | 这个功能非常有用,我经常使用。 325 | 还有一个我在日常使用中发现非常有价值的撤销功能的一个很好的补充是,vim内置的一个功能,就是你可以指定一个撤销目录。 326 | 如果你指定了一个撤销目录,默认情况下,如果你没有启用这个功能,每次进入一个文件时,你的撤销历史记录都是干净的。 327 | 当你在文件中进行修改并撤消操作时,你会创建一个历史记录,但一旦你退出文件,它就会丢失。 328 | 对不起,一旦你退出vim,它就会丢失。 329 | 然而,如果你设置了一个undodir,vim将持久化所有这些更改到这个目录中,所以无论你进入和离开多少次,历史记录都将被保存,并且这非常有帮助,因为它可以帮助你保持文件的更新。 330 | 如果你经常修改某些文件,你可以跟踪它们的变化。 331 | 但有时候也很有帮助,例如,如果你修改了你的bashrc,然后五天后发现某些东西坏了,然后你又使用vim。 332 | 如果你没有版本控制,你可以查看撤消记录,这就是实际发生的事情。 333 | 最后,值得熟悉vim使用的不同特殊寄存器,例如,如果你想复制/粘贴一个东西,它会进入一个特定的寄存器,如果你想使用类似于操作系统剪贴板的a OS a复制,你应该从不同的寄存器中复制或粘贴,寄存器有很多种。 334 | 我认为你应该探索一下,有很多关于寄存器的知识需要了解。 335 | 下一个问题是关于双因素身份验证的,出于时间考虑,我将简单回答一下。 336 | 对于任何安全敏感的事情,使用双因素身份验证是值得的。 337 | 338 | 我用双因素认证来保护我的GitHub账号和邮箱等安全敏感信息。 339 | 有很多不同类型的双因素认证,从基于短信的双因素认证,当你尝试登录时,会向你发送一个特殊的数字,你必须输入这个数字,到像通用双因素认证这样的工具,比如那些需要插入Yubikey的东西,每次登录都需要轻触。 340 | 并不是所有的双因素认证都是一样的,你真的想使用像U2F这样的认证,而不是基于短信的认证。 341 | 有些基于一次性密码的认证,你需要输入这个密码,我们没时间深入讨论为什么有些方法比其他方法更好,但在高层次上使用U2F。 342 | 互联网上有很多解释为什么其他方法不是一个好主意。 343 | 现在网页浏览器之间的差异越来越少了。 344 | 几乎所有的网页浏览器都是Chrome,或者是使用与Chrome相同的浏览器引擎的浏览器。 345 | 这有点让人难过,但我认为如果您想要一个更可定制或不想被Google绑架的安全浏览器,Chrome是一个很好的选择,那么使用Firefox,不要使用Safari,它是Chrome的更糟糕的版本。 346 | 新的Internet Explorer Edge也很不错,也使用与Chrome相同的浏览器引擎,这可能是可以接受的,但如果可以的话还是要避免它,因为它有一些像遗留模式的东西,你不想处理。 347 | 还有一个很酷的新浏览器叫做flow,你目前不能用它做什么有用的事情,但他们正在编写自己的浏览器引擎,这真的很棒。 348 | Firefox也有一个叫做Servo的项目,他们正在使用Rust实现他们的浏览器引擎,以便使其超级并发。 349 | 他们已经开始从该版本中获取模块,并将它们移植到gecko中,gecko是Firefox的主要浏览器引擎,以便在那里获得速度提升,这是一个很酷的东西,你可以关注一下。 350 | 这就是所有的问题,嘿,我们做到了。 351 | 很好,我想感谢你参加了这门缺失的学期课程,我们明年再见。 352 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture04_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 好的,欢迎来到今天的讲座,我们将会讲解数据整理。 2 | "数据整理"这个词组可能听起来有点奇怪,但它的基本想法是,你有一个格式的数据,而你想要它在另一种不同的格式中,这种情况时常发生。 3 | 我不仅指转换图像,还可能是你有一个文本文件或日志文件,但你实际上希望以某种其他格式获得这些数据,例如你想要一个图形或对数据进行统计。 4 | 任何从一段数据到另一种表示形式的过程都可以被称为"数据整理"。 5 | 在本学期前面的某些课程中,我们已经看到了一些这种数据整理的例子,例如当你使用管道操作符时,它可以让你从一个程序的输出中获取数据并将其传递给另一个程序,你就是以某种方式进行数据整理。 6 | 但是在这节课上,我们将会看到更高级和更实用的数据整理方法。 7 | 然而,要进行任何类型的数据整理,你需要有一个数据源。 8 | 你需要先有一些数据才能进行操作。 9 | 这样的数据很多种,我们在今天的讲座笔记的练习部分中给出了一些例子。 10 | 在这个例子中,我将使用一个系统日志。 11 | 我有一台服务器在荷兰运行,因为那时候看起来是个合理的选择。 12 | 在那台服务器上,它运行了一个常规的日志守护进程,这是Linux的一个相对标准的日志机制,有一个名为"journal CTL"的命令,可以让你查看系统日志。 13 | 所以我将对该日志进行一些转换,看看是否可以从中提取出一些有趣的东西。 14 | 然而,你会看到,如果我运行这个命令,我会得到很多数据,因为这个日志包含了很多东西,对于我的服务器来说,这个日志记录了从一月一日开始发生的事情,而且有些日志甚至记录了更早的事件。 15 | 有很多东西。 16 | 所以我们要做的第一件事情是尝试将它限制到只有一个内容。 17 | ``` 18 | 好的,欢迎来到今天的讲座,我们将要讲解数据整理。 19 | 虽然"数据整理"这个词组可能听起来有些奇怪,但它的基本想法是,你有一份格式化的数据,但你希望将它转换成另一种不同的格式。这种情况时常发生。这种数据的格式转换可能涉及到图像转换,也可能是将文本文件或日志文件转换成另一种格式,例如图形或者统计数据。 20 | 任何将一段数据转换成另一种表示形式的过程都可以被称为"数据整理"。 21 | 在本学期前面的一些课程中,我们已经看到了一些数据整理的例子,例如当你使用管道操作符时,它可以让你从一个程序的输出中获取数据并将其传递给另一个程序,以便以某种方式进行数据整理。 22 | 但是在这节课上,我们将会看到更高级和更实用的数据整理方法。 23 | 然而,在进行任何类型的数据整理之前,你需要有一个数据源。你需要先有一些数据才能进行操作。这样的数据有很多种,我们在今天的讲座笔记的练习部分中给出了一些例子。在这个例子中,我将使用一个系统日志。 24 | 我有一台服务器在荷兰运行,因为那时候看起来是个合理的选择。在那台服务器上,它运行了一个常规的日志守护进程。这是Linux的一个相对标准的日志机制,有一个名为"journal CTL"的命令,可以让你查看系统日志。因此,我将对该日志进行一些转换,看看是否可以从中提取出一些有趣的东西。 25 | 然而,你会发现,如果我运行这个命令,我会得到很多数据,因为这个日志包含了很多内容。对于我的服务器来说,这个日志记录了从一月一日开始发生的所有事件,有些日志甚至记录了更早的事件。因此,我们需要做的第一件事情就是尝试将它限制到只有一个内容。 26 | ``` 27 | 28 | 这里我们要用到grep命令。 29 | 我们将把这个命令通过grep管道进行传递,并用它来搜索SSH。 30 | SSH指的是一种通过命令行远程访问计算机的方式。 31 | 特别是当您将服务器放在公共互联网上时,全世界的人都会试图连接并登录以接管您的服务器。 32 | 因此,我想查看这些人试图如何做到这一点,所以我要使用grep搜索SSH。 33 | 您很快就会看到,这也会生成大量内容。 34 | 至少在理论上,这会非常慢。 35 | 这将生成大量内容,很难甚至只是可视化这里正在发生的事情。 36 | 因此,让我们只查看人们尝试登录我的服务器时使用的用户名。 37 | 所以,您会看到其中一些行显示为已断开连接,已断开连接的无效用户,然后是某个用户名。 38 | 我只需要这些行,这就是我真正关心的。 39 | 不过,我将再做一次更改,如果您考虑此管道的工作方式,如果我在这里做到“已连接”,那么此管道底部的操作将会将整个日志文件通过网络发送到我的机器,然后在本地运行grep来仅查找包含ssh的行,然后在本地进一步过滤它们。 40 | 这似乎有点浪费,因为我不在乎大多数这些行,而且远程站点也在运行shell,所以我实际上可以在服务器上运行整个命令。 41 | 因此,我告诉您,SSH,我要您在服务器上运行这三个管道操作,然后将返回的内容通过less管道进行传递。 42 | ``` 43 | 在这里,我们将使用grep命令。 44 | 我们将使用grep管道将该命令传递,并用它来搜索SSH。 45 | SSH是一种通过命令行远程访问计算机的方式。 46 | 特别是当您将服务器放在公共互联网上时,全世界的人都会试图连接并登录以接管您的服务器。 47 | 因此,我想查看这些人试图如何做到这一点,所以我要使用grep搜索SSH。 48 | 很快您就会看到,这将生成大量内容。 49 | 至少在理论上,这会非常慢。 50 | 这将生成大量内容,很难甚至只是可视化这里正在发生的事情。 51 | 因此,让我们只查看人们尝试登录我的服务器时使用的用户名。 52 | 所以,您会看到其中一些行显示为已断开连接的无效用户,然后是某个用户名。 53 | 我只需要这些行,这就是我真正关心的。 54 | 不过,我还要进行一些修改。如果您考虑此管道的工作方式,如果我在这里加上“已连接”,那么整个日志文件都会通过网络发送到我的机器,然后在本地运行grep来仅查找包含ssh的行,然后在本地进一步过滤它们。这似乎有点浪费,因为我不关心大多数这些行,而且远程站点也在运行shell,所以我实际上可以在服务器上运行整个命令。 55 | 因此,我会告诉SSH,在服务器上运行这三个管道操作,然后通过less管道将返回的内容传递回来。 56 | ``` 57 | 58 | 这个操作是什么意思呢?它将进行与我们之前筛选日志相同的筛选,但是它将在服务器端执行,服务器只会发送我关心的那些行给我。 59 | 然后,当我将其本地通过叫做less的程序进行管道连接时,less是一个分页程序。 60 | 你已经看到过一些例子,比如当你输入man命令时,它会在一个分页程序中打开,分页程序是一种方便的方式,可以将长篇内容适应到你的终端窗口中,并让你滚动和浏览,以便不会直接滚过你的屏幕。 61 | 因此,如果我运行这个命令,它仍然需要一段时间,因为它必须解析大量的日志文件,并且特别地,grep正在缓冲,因此它决定是相对不可靠的。 62 | 我可以尝试不加grep参数来运行,看看是否更有帮助。 63 | 它为什么不想帮助我呢?好吧,我要作弊一点,忽略我。 64 | 或者因为互联网速度真的很慢。 65 | 幸运的是,有一个解决办法,因为之前我已经运行了以下命令。 66 | 因此,这个命令将匹配断开连接的所有SSH日志条目的输出,并将其保存在我电脑上的一个文件中。 67 | 好的,我在我的办公室运行了这个命令,这样做的目的是下载所有与"disconnect from"匹配的SSH日志条目,因此我本地拥有这些内容,这非常方便,对吧?没有必要每次都流式传输整个日志,因为我知道我将来要操作的内容一定是这些起始模式。 68 | 因此,我们可以查看SSH.log文件,你会看到有很多很多行,都说"disconnected from","invalid user","authenticating users"等等。 69 | 所以这些是我们需要处理的行,这也意味着未来,我们不必经过整个SSH过程。 70 | 我们只需要查看那个文件,然后直接操作它。 71 | 因此,在这里我还可以演示一下这个分页程序,如果我运行cat SSH.log并将其通过less管道连接,它会给我一个分页程序,我可以上下滚动。 72 | 可以把它变小一点,这样我可以通过这个文件浏览,我可以使用大致上的Vim绑定来进行操作,比如控制U向上滚动,控制D向下滚动,Q退出。 73 | ``` 74 | 这个操作的意思是什么呢?它将进行与之前筛选日志相同的筛选,但是它将在服务器端执行,只会将我关心的那些行发送给我。 75 | 然后,当我将其本地通过一个叫做less的程序进行管道连接时,less是一个分页程序。你已经看到过一些例子,比如当你输入man命令时,它会在一个分页程序中打开,分页程序是一种方便的方式,可以将长篇内容适应到你的终端窗口中,并让你滚动和浏览,以便不会直接滚过你的屏幕。 76 | 因此,如果我运行这个命令,它仍然需要一段时间,因为它必须解析大量的日志文件。特别地,grep正在缓冲,因此它决定是相对不可靠的。我可以尝试不加grep参数来运行,看看是否更有帮助。为什么它不想帮助我呢?好吧,我要作弊一点,忽略我,或者因为互联网速度真的很慢。 77 | 幸运的是,有一个解决办法,因为之前我已经运行了以下命令。这个命令将匹配所有与“disconnect from”相关的SSH日志条目,并将其保存在我电脑上的一个文件中。 78 | 好的,我在我的办公室运行了这个命令,这样做的目的是下载所有与“disconnect from”匹配的SSH日志条目,因此我本地拥有这些内容,这非常方便,对吧?没有必要每次都流式传输整个日志,因为我知道我将来要操作的内容一定是这些起始模式。 79 | 因此,我们可以查看SSH.log文件,你会看到有很多很多行,都说“disconnected from”,“invalid user”,“authenticating users”等等。这些是我们需要处理的行,这也意味着未来,我们不必经过整个SSH过程。我们只需要查看那个文件,然后直接操作它。 80 | 在这里我还可以演示一下这个分页程序。如果我运行cat SSH.log并将其通过less管道连接,它会给我一个分页程序,我可以上下滚动。可以把它变小一点,这样我可以通过这个文件浏览。我可以使用大致上的Vim绑定来进行操作,比如控制U向上滚动,控制D向下滚动,Q退出。 81 | ``` 82 | 83 | 这依然是很多的内容,而且这些行包含了我并不感兴趣的垃圾信息。 84 | 我真正想看到的是这些用户名。 85 | 这里,我们要开始使用的工具叫做 sed。 86 | Sed 是一个流编辑器,它修改了一个更早的程序叫做 edie,后者是一个非常奇怪的编辑器,你们可能都不想使用。 87 | 是的,Oh tsp 是我正在连接的远程计算机的名称。 88 | 因此,sed 是一个流编辑器,它基本上允许你修改流的内容。 89 | 你可以将其视为做替换,但实际上它是在流上运行的一个完整的编程语言。 90 | 然而,你用 sed 最常做的事情之一就是在输入流上运行替换表达式。 91 | 这是什么样子的呢?好的,让我给你展示一下。 92 | 这里,我将它管道到 sed,然后我会说我想要删除“disconnected from”之前的所有内容。 93 | 这可能看起来有点奇怪。 94 | 观察到的是,SSH 守护程序的日期、主机名和进程 ID,我不在意。 95 | 我可以直接删除它,还可以删除“disconnected from”那一部分,因为似乎每个日志条目中都包含这个。 96 | 因此,我编写了一个 sed 表达式。 97 | 在这种情况下,它是一个 S 表达式,即一个替换表达式。 98 | 它有两个参数,基本上是包含在这些斜杠中的。 99 | 因此,第一个是搜索字符串,第二个是目前为空的替换字符串。 100 | 所以,在这里,我是说搜索以下模式并将其替换为空格,然后在最后将其管道到 less 中。 101 | 你看到了吗?现在它已经删除了所有这些行的开头,正则表达式。 102 | 103 | 这看起来非常方便。 104 | 但你可能会想,我构建的这个模式是什么意思?这是一个正则表达式的例子。 105 | 在编程中你可能已经接触过正则表达式,但是一旦你进入命令行,你会发现自己经常使用它来进行数据处理。 106 | 正则表达式本质上是一种强大的匹配文本的方法。 107 | 你可以用它来匹配其他东西,但文本是最常见的例子。 108 | 在正则表达式中,有许多特殊字符,它们不仅可以匹配单个字符,还可以匹配特定类型的字符或一组选项。 109 | 它本质上为你生成一个程序,用于搜索给定的文本。 110 | 例如,句点(dot)表示任何单个字符,如果你在一个字符后面加上星号(star),它就表示该字符的零个或多个。 111 | 因此,在这种情况下,这个模式表示零个或多个任何字符,后面跟着字面上的字符串“disconnected from”。 112 | 我要求匹配它,然后用空格替换它。 113 | 正则表达式有许多这种特殊字符,具有各种不同的含义。 114 | 你可以利用它们。 115 | 我提到了星号(star),表示零个或多个,还有加号(Plus),表示一个或多个。 116 | 这表示我想要前面的表达式至少匹配一次。 117 | 你还可以使用方括号来匹配许多不同的字符。 118 | 所以在这里,让我们建立一个字符串列表,像AB这样,我想用空格替换A和B。 119 | 120 | 好的,那么这里我告诉模式要做的是用空替换任何A或B字符。 121 | 所以如果我把第一个字符变成B,它仍然会产生BA。 122 | 你可能会想,为什么它只替换了一次?这是因为正则表达式会做的事情,特别是在这种默认模式下,它们只会匹配一次模式,然后在每一行上应用一次替换。 123 | 这就是sed通常做的事情。 124 | 你可以提供G修饰符,它表示尽可能多次匹配,这种情况下会删除整行,因为每个字符都是A或B。 125 | 如果我在这里添加了一个C并移除除C以外的所有字符,那么中间的其他字符都将被保留,但是任何A或B都会被删除。 126 | 你也可以像对这个添加修饰符。 127 | 例如,这会做什么?它表示我想要零个或多个AB字符串,然后我要用空替换它们。 128 | 这意味着如果我有一个独立的A,它将不会被替换。 129 | 如果我有一个独立的B,它将不会被替换,但是如果我有AB字符串,它将被删除,这是因为sed很愚蠢。 130 | 这里的-a是因为sed是一个非常古老的工具,因此它仅支持非常旧的正则表达式版本。 131 | 通常,您需要使用-E(大写E)运行它,这使它使用更现代的语法来支持更多的功能。 132 | 如果你在无法使用的地方,你必须在括号前加上反斜杠,以表示“我要特殊的括号含义”,否则,它们只会匹配文字括号,这可能不是你想要的。 133 | 注意,这里替换了AB,这里替换了AB,但是它留下了这个C,而且它也留下了最后的A,因为这个A不再匹配这个模式了。 134 | 你可以按任何方式组合这些模式。 135 | 你也有类似于替换的东西。 136 | 137 | 你可以说任何匹配AB或BC的内容,我要删除它们。 138 | 你会发现这个AB已经被删除了,而这个BC虽然也符合模式,但因为AB已经被删除了,所以它没有被删除。 139 | 这个AB被正确地删除了,但C仍然保留在原地。 140 | 这个ab被删除了,而这个c被保留了,因为它仍然不匹配。 141 | 如果我这样做,如果我删除这个a,那么现在这个aB模式就不会匹配到这个B,所以它会被保留,然后BC将匹配BC,就会被删除。 142 | 当你第一次接触到正则表达式时,它们可能会非常复杂,即使你对它们有更多的经验,看起来仍然会让人望而生畏。 143 | 这就是为什么通常需要使用类似于正则表达式调试器这样的工具,我们稍后会介绍。 144 | 但首先,让我们试着制定一个能够匹配日志并且匹配到目前为止我们一直在处理的日志的模式。 145 | 所以在这里,我将从这个文件中提取出几行,比如前五行。 146 | 现在这些行看起来都是这样的,对吧?我们想要做的是只留下用户名。 147 | 那么这可能是什么样子呢?好的,我们可以试着做一件事情。 148 | 但是,让我先拿出一行内容,比如说“disconnected from invalid user disconnected from maybe four to one one whatever.”,这是一个登录行的例子,其中有人尝试使用用户名“disconnected from missing an S”登录。 149 | 你会发现这个模式实际上删除了用户名,这是因为当你使用“点星”和任何这些范围表达式、间接表达式时,它们是贪婪的。 150 | 它们会尽可能匹配更多内容。 151 | 所以在这种情况下,这是我们想要保留的用户名,但是这个模式实际上一直匹配到第二次出现它或最后一次出现它,所以它之前的所有内容,包括用户名本身,都被删除了。 152 | 153 | 因此,我们需要想出一个比只使用“点星”更聪明的匹配策略,因为这意味着如果我们遇到特别敌对的输入,我们可能会得到我们意想不到的结果。 154 | 好的,让我们来看看如何匹配这些行。 155 | 让我们从头开始构建这个正则表达式。 156 | 首先,我们知道我们要一个短横线加大写字母E,对吧?因为我们不想到处都要加反斜杠。 157 | 这些行看起来像是说“from”,然后有些行写了“invalid”,但有些没有,对吧?这行写了“invalid”,那个没有。 158 | 这里的问号表示零或一次,所以我想要“invalid space user”的零次或一次。 159 | 还有什么?好的,这里会是一个双空格,所以我们不能有那个。 160 | 然后会有一些用户名,然后会是一个看起来像是IP地址的东西。 161 | 这里我们可以使用我们的范围语法,写零到九和一个点,对吧?这就是IP地址,我们想要很多。 162 | 然后它说“port”,所以我们只需要匹配一个字面上的端口,然后是另一个数字零到九,然后我们会加号一次或多次。 163 | 这里我们还要做的另一件事是在正则表达式中加上锚定。 164 | 所以正则表达式中有两个特殊字符:一个是脱字符或帽子,它匹配行的开头,还有一个是美元符号,它匹配行的结尾。 165 | 所以这里我们要说这个正则表达式必须匹配整个行。 166 | 我们这样做的原因是想象一下,如果有人把他们的用户名设置为整个日志字符串,那么如果你尝试匹配这个模式,它会匹配用户名本身,这不是我们想要的。 167 | 通常,你会希望尽可能地锚定你的模式,以避免那些奇怪的情况。 168 | 好的,让我们看看这给我们带来了什么。 169 | 这删除了许多行,但不是所有行。 170 | 例如,这个行末包括了“pre-off”,所以我们需要去掉它。 171 | 如果有一个空格,“pre-off”,方括号是特殊字符,我们需要转义它们,对吧?现在,让我们看看如果尝试更多行会发生什么。 172 | 173 | 不,它仍然得到了一些奇怪的结果。 174 | 这是因为有些行不为空,也就是说这个模式没有匹配上。 175 | 例如,这一行是“authenticating user”,而不是“invalid user”。 176 | 那么,我们怎样才能匹配“invalid”或“authenticated”出现零次或一次,并紧跟着“user”呢?好的,现在看起来很有希望了,但是这个输出并不是特别有用,对吧?这里我们只是成功地删除了日志文件的每一行,这并不是非常有用的。 177 | 相反,我们真正想要做的是在匹配用户名时,就像这里一样,我们真正想要记住的是用户名,因为那是我们想要打印出来的内容。 178 | 在正则表达式中,我们可以使用捕获组来实现这一点。 179 | 捕获组是一种方式,可以指示我们要记住这个值,并在以后重用它。 180 | 在正则表达式中,任何带括号的表达式都将成为这样的捕获组。 181 | 我们实际上已经有了一个捕获组,就是这个第一个组,现在我们正在创建第二个组。 182 | 请注意,这些括号对匹配没有任何影响,因为它们只是在表示这个表达式是一个整体,但是我们没有在后面加上任何修饰符,所以只匹配一次。 183 | 捕获组之所以有用,是因为您可以在替换时引用它们。 184 | 在这里,我可以说反斜杠二。 185 | 这是指引用捕获组的名称的方式。 186 | 在这种情况下,我是说匹配整行,然后在替换中放入您捕获的第二个捕获组的值。 187 | 请记住,这是第一个捕获组,而这是第二个捕获组。 188 | 这样可以得到所有的用户名。 189 | 现在回顾一下我们写的内容,这非常复杂,对吧?现在我们已经逐步走过了它的每个步骤,知道为什么它必须是这样的,但这并不明显,这就是这些行的工作原理。 190 | 191 | 这就是正则表达式调试器非常有用的地方。 192 | 这里有一个调试器,网上也有很多。 193 | 我已经预先填好了我们刚刚使用的表达式。 194 | 你会注意到它告诉我现在所有的匹配情况。 195 | 这个窗口的字体有点小,但是如果我在这里做一些操作,这个解释会告诉我点-星可以匹配零个或多个字符,然后是“断开连接”,后面是一个捕获组,它会向你展示所有的东西。 196 | 这是一件事,但它还会让你给出一个测试字符串,然后将模式与每个测试字符串匹配,并突出显示不同的捕获组。 197 | 所以,在这里,我们将用户设置为了一个捕获组,对吧?它会说好的,整个字符串都匹配了,所以整个字符串都是蓝色的,所以它匹配了。 198 | 绿色是第一个捕获组,红色是第二个捕获组,这是第三个捕获组,因为“pre-auth”也被放在括号中。 199 | 这是一种方便的方法,可以尝试调试正则表达式。 200 | 例如,如果我输入“断开连接”,然后在这里加一行,让用户名变成“disconnected from”,现在这一行的用户名已经变成“disconnect from”。 201 | 很好,我在这里有一些预见性。 202 | 你会注意到,使用这个模式,这不再是一个问题,因为它匹配了用户名。 203 | 如果我们把整行或整行变成用户名会发生什么?现在会变得非常混乱,对吧?所以这就是为什么正则表达式很难弄对的原因,因为它现在尝试匹配。 204 | 它匹配第一次出现的用户名,或者第二个“invalid”(在这种情况下),因为这是贪婪的。 205 | 我们可以通过在这里加一个问号来使它变成非贪婪。 206 | 207 | 如果你在加号或星号后面加上一个问号,它就变成了一个非贪婪匹配。 208 | 这意味着它将不会尽可能地匹配尽可能多的字符。 209 | 然后你会发现,这个表达式被正确解析了,因为这个点会停在第一个disconnected from,这是SSH实际上会生成并出现在我们的日志中的。 210 | 从这些解释中,你可能可以看出正则表达式可以变得非常复杂,并且你可能必须在模式中应用各种奇怪的修饰符。 211 | 真正学习它们的唯一方法是从简单的表达式开始,然后逐步构建,直到它们匹配你所需的内容。 212 | 通常你只需要处理一些类似于我们在这里提取用户名的一次性工作,而不需要关心所有特殊的条件,对吧?不需要担心某个人的SSH用户名是否完全匹配你的登录格式。 213 | 那可能不是什么重要的事情,因为你只是想找到用户名。 214 | 但是正则表达式确实非常强大,如果你做的是真正重要的事情,你就要小心。 215 | 你提出了一个问题:默认情况下,正则表达式只会按行匹配,不会跨越换行符。 216 | 所以sed的工作方式是每行操作,所以sed将为每一行执行这个表达式。 217 | 好的,关于正则表达式或者这个模式有什么问题吗?这是一个复杂的模式,如果感觉困惑,不要担心。 218 | 之后可以在调试器中查看它。 219 | 请记住,我们在这里假设用户只能控制他们的用户名。 220 | 所以他们能做的最糟糕的事情就是把整个条目都作为用户名。 221 | 看看会发生什么,对吧?这就是它的工作原理。 222 | 原因在于这个问号的作用,它意味着一旦我们遇到断开连接的关键字,就开始解析模式的剩余部分。 223 | 而SSH在用户可以控制任何东西之前就打印了第一次出现的disconnected。 224 | 因此,在这种特定情况下,即使是这种情况也不会混淆模式。 225 | 226 | 如果你在进行数据整理时使用这种奇怪的匹配方式,通常不会涉及到安全问题,但可能会导致你得到非常奇怪的数据。 227 | 如果你要绘制数据图表,可能会漏掉重要的数据点,或者解析出错误的数字,然后你的图表中就会出现原始数据中没有的数据点。 228 | 因此,如果你发现自己在编写复杂的正则表达式,请仔细检查它是否确实匹配了你想要匹配的内容。 229 | 即使与安全无关,正则表达式的模式也可能非常复杂。 230 | 例如,关于如何使用正则表达式匹配电子邮件地址存在很大的争议。 231 | 你可能会想到类似于这样的模式:字母、数字、下划线和百分号,后跟一个加号,因为在 Gmail 中,电子邮件地址中可以包含加号。 232 | 在这种情况下,加号只是用于表示任意数量的这些字符,但至少要有一个,因为一个没有任何内容的电子邮件地址是无效的,而且在域名后面也是一样。 233 | 顶级域名必须至少包含两个字符,并且不能包含数字。 234 | 你可以使用.com,但不能使用.dap7。 235 | 但事实证明,这种方法并不是完全正确的。 236 | 有很多有效的电子邮件地址无法通过这种方式匹配,也有很多无效的电子邮件地址可以通过这种方式匹配。 237 | 因此,有许多建议,有些人已经建立了完整的测试套件来尝试找到最佳的正则表达式,而这个特定的正则表达式是用于 URL 的。 238 | 类似的正则表达式也适用于电子邮件地址,他们发现最好的正则表达式是这个。 239 | 我不建议你试图理解这个模式,但这个模式似乎几乎可以完美地匹配互联网标准中的有效电子邮件地址,包括各种奇怪的 Unicode 代码点。 240 | 241 | 这只是说,正则表达式可能非常复杂,如果你陷入像这样的境地,那么可能有更好的方法。 242 | 例如,如果你发现自己试图解析HTML或JSON等表达式的内容,你应该使用不同的工具。 243 | 还有一个练习,要求你不使用正则表达式完成这个任务。 244 | 如果你想了解它们的工作原理,可以查看讲义。 245 | 现在我们有了用户名列表,让我们回到数据整理。 246 | 对我来说,这个用户名列表还不是很有趣。 247 | 让我们看看有多少行。 248 | 如果我使用wc -l命令,有198000行。 249 | wc是单词计数程序,-l让它计算行数。 250 | 这是很多行。 251 | 如果我开始滚动它们,这仍然没有帮助我。 252 | 我需要统计数据,需要某种聚合,而sed工具对许多事情都很有用,它提供了一个完整的编程语言,可以做奇怪的事情,比如插入文本或只打印匹配的行,但并不一定是万能的工具。 253 | 有时候有更好的工具。 254 | 例如,你可以编写一个行计数器。 255 | 你只是不应该使用sed,除了搜索和替换之外,它是一个糟糕的编程语言,但有其他有用的工具。 256 | 例如,有一个叫做sort的工具。 257 | sort接受一堆输入行,将它们排序,然后将它们打印到输出中。 258 | 因此,在这种情况下,我现在得到了那个列表的排序输出。 259 | 它仍然有20万行,对我来说仍然不是非常有用,但现在我可以将它与一个叫做uniq的工具组合使用。 260 | 261 | uniq会查看一个排序后的行列表,并仅打印那些唯一的行。 262 | 因此,如果有多个相同的行,则仅打印一次。 263 | 然后我可以使用uniq -c,这将计算任何重复行的重复次数并消除它们。 264 | 这是什么样子?好的,如果我运行它,需要一段时间。 265 | 有13个zze用户名,有10个ZXVF用户名等等。 266 | 我可以滚动浏览这个列表。 267 | 这仍然是一个非常长的列表,但至少现在它比以前更整理了一些。 268 | 让我们看看现在我有多少行。 269 | 好的,有24,000行。 270 | 它仍然太多,对我来说不是有用的信息,但是我可以使用更多的工具来缩小范围。 271 | 例如,我可能关心哪些用户名被最多使用。 272 | 那么,我可以再次使用sort,然后我可以说我想在输入的第一列上进行数字排序,因此-n表示数字排序,-K允许您选择要按其排序的输入中的一个空格分隔列。 273 | 我在这里给出一个逗号,是因为我想从第一列开始并在第一列停止。 274 | 或者,我可以说我要按这个列列表进行排序,但在这种情况下,我只想按该列排序。 275 | 然后我只想要最后的十行。 276 | 因此,默认情况下,sort会按升序输出,因此具有最高计数的那些将位于底部,然后我只想要最后的十行。 277 | 现在当我运行这个命令时,我实际上得到了一些有用的数据。 278 | 对,它告诉我有11,000次使用用户名root的登录尝试,有4,000次使用用户名123456等等。 279 | 这非常方便,对吧?现在突然间这个巨大的日志文件实际上为我提供了有用的信息。 280 | 这就是我真正想从那个日志文件中得到的信息。 281 | 282 | 现在,也许我只想快速禁用我的机器上SSH登录的root用户,这是我建议你也要做的。 283 | 在这种情况下,我们实际上不需要-k4排序,因为默认情况下,sort会按整行排序,而数字恰好排在第一位。 284 | 但是了解这些额外的标志是有用的,您可能会想知道,我怎么知道这些标志存在?我怎么知道这些程序甚至存在?好吧,通常是从像这样的课程中得到的信息。 285 | 标志通常是“我想按照不是整行的某些东西进行排序。 286 | ”您的第一反应应该是键入man sort,然后阅读页面,很快就会告诉您,“这是如何选择一个非常好的列。 287 | 这是如何按数字进行排序的。 288 | ”好的,如果现在我有了这个前20名的名单,假设我实际上并不关心计数,我只想要一个逗号分隔的用户名列表,因为我要每天通过电子邮件发送给自己之类的东西,例如:“这是前20个用户名。 289 | ”那么我可以这样做。 290 | 这是很多奇怪的命令,但它们是值得知道的命令。 291 | awk是一种基于列的流处理器。 292 | 所以我们谈论过sed,它是一种流编辑器,所以它主要尝试编辑输入中的文本。 293 | 另一方面,awk也让您编辑文本。 294 | 它仍然是一种完整的编程语言,但它更专注于列数据。 295 | 因此,在这种情况下,awk默认会解析其输入的空格分隔列,然后单独操作这些列。 296 | 在这种情况下,我正在说仅打印第二列,也就是用户名。 297 | Paste是一个命令,它将一堆行粘在一起成为一行,这是用逗号分隔符“-s”表示的。 298 | 因此,在这种情况下,对于这个问题,我想获得一个逗号分隔的前用户名列表,然后我可以做任何有用的事情。 299 | 也许我想将其放入不允许用户名的配置文件中或类似的内容。 300 | 值得更多地讨论awk,因为它被证明是这种数据整理的一个非常强大的语言。 301 | 我们简要提到了“print $2”做了什么,但事实证明,对于awk,您可以做一些非常非常复杂的事情。 302 | 303 | 例如,让我们回到我们只有用户名的地方。 304 | 我认为我们仍需要使用sort和unique,因为否则列表会变得太长,而且我只想打印与特定模式匹配的用户名。 305 | 例如,我想查看所有仅出现一次且以C开头以e结尾的用户名。 306 | 虽然这是一件奇怪的事情,但总体来说,它非常简单易懂。 307 | 我可以说我希望第一列为1,第二列匹配以下正则表达式。 308 | 嘿,这可能只是一个点,然后我想打印整个行。 309 | 所以,除非我弄错了什么,这将给我所有以C开头以e结尾并且在日志中仅出现一次的用户名。 310 | 现在,这可能对数据并不是非常有用。 311 | 我在这个讲座中试图向您展示可用的工具类型,而在这种特定情况下,即使我们所做的事情很奇怪,这种模式也不是很复杂。 312 | 这是因为在Linux上,特别是在命令行工具中,工具通常是基于输入行和输出行构建的,而这些行通常会有多列,而awk非常适合操作列。 313 | 现在,awk不仅可以像每行匹配那样做事情,而且还可以让你做一些事情,例如,我想知道这些的数量。 314 | 我想知道有多少用户名与此模式匹配。 315 | 好的,WCHL可以正常工作。 316 | 有31个这样的用户名,但awk是一种编程语言。 317 | 这是您可能永远不会自己做的事情,需要了解这些。 318 | 319 | 但是重要的是要知道你可以做到。 320 | 有时候这样做实际上是有用的。 321 | 我刚刚意识到这可能在我的屏幕上很难阅读,让我尝试在一秒钟内修复它。 322 | 让我们开始……是的,显然鱼不想让我这样做。 323 | 那么,这里开始是一个只匹配第零行的特殊模式。 324 | 结束是一个只在最后一行之后匹配的特殊模式。 325 | 然后这将是一个正常的模式,用于匹配每一行。 326 | 所以我在这里所说的是,在第零行上,将变量rose设置为零。 327 | 在每行匹配此模式时,增加rose的值。 328 | 在匹配了最后一行之后,打印rose的值。 329 | 这将具有与运行WCHL相同的效果,但全部使用awk。 330 | 像WCHL这样的特定实例非常好,但有时您可能想要像保留词典或地图之类的内容。 331 | 您可能想要计算统计信息。 332 | 您可能想要这样做,例如:我想要此模式的第二个匹配项。 333 | 因此,您需要一个有状态的匹配器,可以忽略第一个匹配项,但然后打印第二个匹配项之后的所有内容。 334 | 对于此类简单的awk编程,可能很有用。 335 | 实际上,我们可以在这种模式中,摆脱最初用于生成此文件的said,sort,unique和grep,并全部使用awk来完成。 336 | 但您可能不想这样做。 337 | 这可能会太痛苦而不值得。 338 | 值得稍微谈谈您可能想要在命令行上使用的其他工具类型。 339 | 其中之一是一个非常方便的程序,称为BC。 340 | 341 | BC是伯克利计算器,我想。 342 | Man BC。 343 | 我认为BC最初来自伯克利计算器。 344 | 它是一个非常简单的命令行计算器,但是它不会给你提示符,而是从标准输入中读取。 345 | 所以我可以像这样做:echo 1加2,然后将其管道传递给BC。 346 | Shell因为许多这些程序通常在愚蠢模式下运行,因此它们不是很有帮助。 347 | 所以它在这里打印了3。 348 | 哇,非常令人印象深刻。 349 | 但是事实证明这可以非常方便。 350 | 想象一下你有一个包含一堆行的文件,比如说,我不知道,这个文件。 351 | 假设我想要求出登录次数,用户名仅被使用一次的数量。 352 | 好的,那么计数不等于1的用户名,我只想打印出计数。 353 | 对的,这是我,给我所有非单次使用用户名的计数。 354 | 然后我想知道这些有多少个。 355 | 请注意,我不能只数行,因为每行都有数字。 356 | 我想求和。 357 | 好的,我可以使用paste将其粘贴到加号中。 358 | 因此,将每行都粘贴到加号表达式中。 359 | 现在这是一个算术表达式,因此我可以将其通过BCL管道传递。 360 | 现在有191,000次登录与至少一个其他登录共享用户名。 361 | 再次说明,这可能不是你真正关心的事情,但这只是为了向你展示你可以相当容易地提取这些数据。 362 | 你还可以用这个做很多其他的事情。 363 | 例如,有一些工具可以让你对输入进行统计分析。 364 | 因此,对于我刚刚打印的这个数字列表,我可以做一些像使用R这样的事情。 365 | R是一种专门用于统计分析的单独编程语言。 366 | 我可以说,让我看看我是否做对了......这又是一种你需要学习的不同编程语言,但如果你已经了解R,或者你也可以将它们通过其他语言管道传递,比如这样。 367 | 368 | 这个命令会在输入流中给我一个汇总统计数据。 369 | 因此,每个用户名的登录尝试中位数为3,最大值为10,000(这是我们之前看到的路由),并告诉我平均值为8。 370 | 对于这个特定的实例,这可能并不重要,这些可能不是有趣的数字,但是如果您正在查看来自基准测试脚本或其他一些数值分布的输出,并且想要查看它们,这些工具非常有用。 371 | 我们甚至可以进行一些简单的绘图。 372 | 因此,这里有一些数字。 373 | 让我们回到我们的sort和k-11,并只查看前两个5。 374 | GNU绘图器是一种让您从标准输入中获取内容的绘图器。 375 | 我并不希望您了解所有这些编程语言,因为它们确实是自己的编程语言,但只是向您展示可能的内容。 376 | 现在,这是自1月1日以来我的服务器上使用前5个用户名的次数的直方图,这只是一个命令行。 377 | 这是一个有点复杂的命令行,但它只是一个命令行,您可以这样做。 378 | 还有两种特殊类型的数据整理,我想在我们剩下的时间中与您谈论的第一种是命令行参数整理。 379 | 有时,您可能会像我们在上一堂课中看到的那样,拥有像查找这样的东西,产生文件列表或者可能产生基准测试脚本参数列表的内容,例如您想使用特定的参数分布运行它。 380 | 假设您有一个脚本,打印运行特定项目的迭代次数,并且您希望像指数分布一样打印每行的迭代次数,并且您要为每个迭代次数运行基准测试。 381 | 382 | 好的,这里有一个工具叫做xargs,它是你的好朋友。 383 | xargs可以将输入的每行转换成参数。 384 | 这可能看起来有些奇怪。 385 | 我来举一个例子。 386 | 我用Rust编程,而Rust可以安装多个版本的编译器。 387 | 所以在这种情况下,你可以看到我安装了稳定版、beta版和几个较早的稳定版本,并启动了不同的日期的Nightly版本。 388 | 这些都非常好,但是随着时间的推移,我不需要去年3月的Nightly版本了。 389 | 我可能想偶尔清理一下这些版本,或许我想清理一下这些东西。 390 | 好吧,这是一系列行的列表,所以我可以先找出Nightly版本,然后把它们删掉。 391 | -V表示不匹配,我不想匹配到当前的Nightly版本。 392 | 好的,这是一个日期为Nightly的列表。 393 | 也许我只想要2019年的版本,现在我想为我的机器卸载这些工具链。 394 | 我可以手动复制每个工具链的名称并粘贴到rust up tool chain remove或uninstall,对吧?但是我现在有这个列表,手动输入会变得非常烦人。 395 | 所以,我想删除它添加的这种后缀,现在只剩下名称,然后我使用xargs。 396 | 因此,xargs将输入列表转换成参数。 397 | 所以我想把这些参数传递给rust up tool chain uninstall,并为了我的方便,我会加上echo以便查看要运行的命令是什么。 398 | 399 | 嗯,这些信息可能不是很有用,但至少你可以看到即将执行的命令。 400 | 如果我去掉这个echo,就是rust up tool chain uninstall,然后是Knightley的列表作为该程序的参数。 401 | 这样,如果我运行它,就会卸载所有工具链,而不必复制粘贴它们。 402 | 因此,这是一个例子,说明这种数据整理实际上可以用于除了查看数据以外的其他任务。 403 | 它只是从一个格式转换到另一个格式。 404 | 您也可以整理二进制数据。 405 | 因此,一个很好的例子是一些视频和图像,您可能真的希望以某种有趣的方式对它们进行操作。 406 | 例如,有一个叫做ffmpeg的工具。 407 | ffmpeg用于编码和解码视频,以某种程度上的图像。 408 | 我将设置它的日志级别为panic,否则它会打印很多东西。 409 | 我希望它从dev video 0读取,这是我的网络摄像头设备的视频,我想要获取第一帧,所以我只想要一张图片,而不是单帧视频文件,然后我希望打印它的输出。 410 | -通常是告诉程序使用标准输入或输出而不是给定文件的方式。 411 | 因此,它在这里期望一个文件名,而文件名-在这种情况下表示标准输出。 412 | 然后,我想通过一个叫做convert的参数将其导管。 413 | convert是一个图像处理程序。 414 | 我想告诉convert从标准输入读取,并将图像转换为灰色空间,然后将结果图像写入文件-,这是标准输出。 415 | 我不想将其导管到gzip中;我们将只压缩这个图像文件,这也将只在标准输入和标准输出上操作。 416 | 然后,我将把它导管到我的远程服务器,并在那里解码该图像,然后将该图像的副本存储下来。 417 | 418 | 记住,tee 会读取输入,将其打印到标准输出和文件中。 419 | 这将复制解码后的图像文件,并继续将其流出。 420 | 现在我将把它带回本地流,并在图像显示器中显示。 421 | 让我们看看是否有效。 422 | 嘿,好的,现在它通过服务器进行了往返,然后通过管道返回,至少在理论上,我的服务器上有一份未压缩的文件的副本。 423 | 让我们看看它是否存在:在这里,CPT's p copy PNG 2 和 CP 8,是的,嘿,同样的文件出现在了服务器上,我们的管道工作了。 424 | 再次强调,这是一个有点儿傻的例子,但它让你看到了构建这些管道的强大之处,这些管道不必是文本数据,它只是将任何格式的数据从一种格式转换为另一种格式。 425 | 例如,如果我想,我可以使用cat dev video 0,然后将其传送到Anish控制的服务器上,然后他可以通过将其输入到他机器上的视频播放器中观看该视频流。 426 | 如果我们想要写入,我们只需要知道这些东西的存在。 427 | 这个实验室有一些练习题,其中一些依赖于你拥有一个看起来有点像Mac OS和Linux上的日志的数据源。 428 | 我们提供了一些命令,供你尝试实验,但请记住,你使用的数据源不是那么重要。 429 | 更重要的是找到一些数据源,你认为可能存在一些有趣的信号,然后尝试从中提取一些有趣的东西,这就是所有练习的目的。 430 | 431 | 因为周一是马丁·路德·金纪念日,所以下一次课是星期二,讲述命令行环境。 432 | 大家对我们目前讲解的内容或是管道或是正则表达式有什么问题吗?我强烈推荐你们学习正则表达式,它们非常方便,不仅在这里使用,而且在编程中也很实用。 433 | 如果有任何问题,请来办公时间,我们会帮助你们的。 434 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture01_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 大家好,感谢你们的到来。 2 | 这门课程叫做“计算机科学教育中缺失的部分”,至少我们是这样称呼这门课的。 3 | 如果你不是来上这门课的话,那么你可能来错地方了。 4 | 我们将在这里待大约一个小时,来给大家介绍一下这门课的内容。 5 | 首先我想跟大家聊一下我们为什么要开设这门课。 6 | 这门课源于我们(Anish、Jose和我)在麻省理工学院担任助教时的一个观察,我们发现我们作为计算机科学家,都知道计算机在处理重复任务和自动化方面非常出色。 7 | 但是我们通常没有意识到,有很多工具可以使我们的开发过程变得更加高效。 8 | 我们可以更好地利用计算机,将其作为自己的工具,而不仅仅是用来搭建网站或制作软件等。 9 | 这门课程是为了解决这个问题,尝试向大家展示一些工具,这些工具可以在你的日常工作、研究和学习中发挥重要的作用。 10 | 这门课程将教授大家如何充分利用你已经了解的工具,同时也会向你介绍一些你之前不知道的工具,并教你如何将这些工具结合起来,以让你变得更加强大。 11 | 这门课程将分为11个一小时的讲座,每个讲座都将涵盖特定的主题。 12 | 你可以查看网站上的课程主题列表,以及每个讲座的日期。 13 | 他们大多数会是独立的,所以你可以选择参加你感兴趣的讲座,但我们会假定你一直在跟进,这样当我们到后面的讲座时,我不会再教你例如bash这样的东西。 14 | 我们还会在线发布讲座笔记和讲座录像。 15 | 我们还没有确定具体时间,但这将是在讲座之后。 16 | 显然,视频必须在讲座之后发布。 17 | 本课程将由我,Jose和Anish一起授课,他们坐在那边,Jose目前不在这里,但他将主持明天的讲座。 18 | 请注意,我们将在仅仅11个一小时的讲座中涵盖大量内容,所以我们将相对快速地讲解。 19 | 但如果有任何地方你感觉跟不上,一定要停止我们。 20 | 如果你觉得有些东西希望我们花更多的时间讲解, 21 | 请随时中断我们提问。 22 | 此外,每次讲座之后,我们都在第32栋楼的第9层办公室有办公时间,即计算机科学楼的Stata Center。 23 | 如果你来Gates Tower的第9层的休息室,那么你可以来做做我们为每次讲座提供的练习,或者询问我们关于讲座中讨论的内容或者如何更高效地使用你的电脑的其他问题。 24 | 由于我们的时间有限,我们将无法对所有工具进行详细介绍,所以只会聚焦有趣的工具以及使用它们的有趣方法。 25 | 我们不一定会深入探讨所有工具的工作原理或更复杂的用例。 26 | 但是,如果你有关于它们的问题,请来问我们。 27 | 其中许多工具我们都使用了多年,我们可能能够教你怎么利用这些工具倒腾一些有趣地东西,所以要充分利用好这次上课的机会. 28 | 这节课我不想说上得非常迅速,但在今天这个讲座中,我们是假定你已经掌握了一些后面的讲座中包含的基础知识,比如如何使用你的shell和终端,对于不熟悉它们的人,我会马上进行解释。 29 | 然后我们会相当快地进入更高级的工具以及如何使用它们。 30 | 你已经可以从讲座笔记中看到我们将要涵盖的主题。 31 | 所以,这就是今天的讲座,所以我们今天要讲shell。 32 | 当你所熟悉的可视化界面不能做想要做的事情时,shell 将是你与计算机交互的主要方式之一。 33 | 因为可视化界面仅限于它们允许你使用的按钮、滑块、输入字段等功能。 34 | 而这些文本工具通常是相互可组合的,而且有许多不同的组合或编程以及自动化的方式。 35 | 这就是为什么,在本课程中,我们将专注于这些基于命令行或文本的工具,而 shell 则是你进行大部分这种工作的地方。 36 | 对于那些不熟悉 shell 的人,大多数平台也都提供一些形式的 shell。 37 | 在 Windows 上,这通常是 PowerShell,但也有其他 shell 可用。 38 | 在 Windows 和Linux 上,你会找到大量的终端。 39 | 这些是能显示 shell 的窗口,并且你也会发现许多不同类型的 shell,其中最常见的是 bash 或 Bourne Again SHell。 40 | 因为bash是非常常见的 shell,所以它也是我们在这些讲座中主要要讲解的。 41 | 如果你在 Mac OS 上,你也可能有 bash,不过打开终端应用程序,可能是一个较旧的版本。 42 | 因此,如果你想在任何这些平台上跟随学习,可以自由选择,但请记住,尽管这些工具在所有平台上都有效,但我们的教学大多数是面向 Linux 的。 43 | 如果你想安装终端和 shell,但不知道该怎么做,我们很乐意在办公时间为你展示,或者你也可以简单地在 Google 上搜索你的平台加terminal,你就可以找到教程。 44 | 现在,当你打开一个终端,你会看到一个类似于这样的界面。 45 | 通常它只有一行在顶部,这就是所谓的 shell 提示符。 46 | 你可以看到我的 shell 提示符看起来像这样,它有我的用户名、我所在机器的名称、我当前所在的路径(我们稍后会讨论路径),然后光标只是闪烁着等待我的输入。 47 | 这就是 shell 提示符,你可以告诉 shell 你想让它做什么。 48 | 你可以自由修改这个shell 提示符 49 | 当你在你的机器上打开它时,它可能不会完全像这样。 50 | 如果你配置了一些东西,它可能看起来像这样,或者它可能看起来完全不同。 51 | 我们不会在这个讲座中太多地讨论自定义你的 shell,我们将在以后讨论。 52 | 这里,我们只会谈论如何使用这个 shell 来做有用的事情。 53 | 这是你与计算机交互的主要文本界面。 54 | 通过这个 shell 提示符,你可以输入命令。 55 | 命令通常是相对简单的东西。 56 | 通常会是像使用参数执行程序之类的事情。 57 | 这是什么样子的呢?我们可以执行一个名为“date”的程序。 58 | 只需要输入“date”,按下回车,然后它会显示日期和时间,这并不令人惊讶。 59 | 你也可以执行带有参数的程序。 60 | 这是修改程序行为的一种方式。 61 | 例如,有一个名为“echo”的程序,它只是打印出你给它的参数。 62 | 参数只是跟随程序名的以空格分隔的东西。 63 | 所以我们可以说“hello”,然后它会打印“hello”回来。 64 | 也许不是特别令人惊讶,但这就是参数的基础。 65 | 66 | 你会注意到我说参数是由空格分隔的,你可能会想,如果我想要一个由多个单词组成的参数怎么办?你也可以加上引号,所以你可以做像“echo hello world”这样的事情,现在 echo 程序接收到一个包含带有空格的字符串“hello world”的参数。 67 | 你也可以用单引号来做这件事,我们会在讨论 bash 脚本时回到单引号和双引号之间的区别。 68 | 你也可以转义单个字符。 69 | 例如,“hello\ world”,这也可以正常工作。 70 | 我们稍后会涵盖有关如何转义和解析各种参数和变量的规则。 71 | 希望你不会遇到太多奇怪的事情。 72 | 只需记住至少有空格分隔参数,所以如果你想做类似于创建名为“my photos”的目录这样的事情,你不能只输入“make directory my photos”。 73 | 它会创建两个目录,一个叫做“my”,另一个叫做“photos”,这可能不是你想要的。 74 | 现在,你可能会问,当我输入“date”或者输入“echo”时,shell是如何知道这些程序是什么的,它们应该做什么呢?答案是,你的计算机有一些内置程序,随着机器一起发货。 75 | 就像你一样,你的机器可能会附带终端应用程序,或者附带Windows Explorer,或者附带某种类型的浏览器。 76 | 它还附带了一堆面向终端的应用程序,这些应用程序存储在你的文件系统中。 77 | 你的shell有一种方法可以确定程序的位置。 78 | 基本上,它有一种搜索程序的方式。 79 | 它通过一种称为环境变量的东西来实现这一点。 80 | 环境变量是一种变量,就像你可能习惯了编程语言中的变量一样。 81 | 事实证明,shell,特别是Bourne-again shell,真的是一种编程语言。 82 | 这里给出的提示不仅能够运行带有参数的程序,你还可以做类似于while循环,for循环,条件语句等的事情...所有这些你都可以在shell中定义函数,变量和所有这些事情。 83 | 我们将在下一讲中涵盖许多这些内容,包括shell脚本。 84 | 但是现在,让我们只看看这个特定的环境变量。 85 | 环境变量是在启动shell时设置的东西;它们不是你每次运行shell时都必须设置的东西。 86 | 有一堆这样的变量被设置了,例如你的主目录在哪里,你的用户名是什么,还有一个对于这个特定目的至关重要的变量,即路径变量。 87 | 因此,如果我echo出dollar path,这将向我显示shell将搜索程序的所有路径列表。 88 | 你会注意到这是一个用冒号分隔的列表。 89 | 它可能有点长而且难以阅读,但本质上是每当您键入程序的名称时,它都会在计算机上搜索这个路径列表,并在每个目录中查找与您尝试运行的命令匹配的程序或文件的名称。 90 | 因此,在我的情况下,当我尝试运行date或echo时,它将逐个遍历这些路径,直到找到包含名为date或echo的程序的路径,然后运行它。 91 | 如果我们想知道它实际运行的是哪个程序,有一个叫做which的命令可以让我们做到这一点。 92 | 因此,我可以键入which echo,它会告诉我,如果我要运行一个名为echo的程序,我将运行这个。 93 | 值得在此停顿一下,谈谈路径是什么。 94 | 路径是一种命名计算机上文件位置的方式。 95 | 在Linux和macOS上,这些路径是由斜杠(正斜杠)分隔的。 96 | 因此,您会看到这是在根目录下,所以开头的斜杠表示从文件系统的顶部开始。 97 | 然后查看名为USR的目录,然后查看名为bin的目录,然后查找名为echo的文件。 98 | 在Windows上,这样的路径通常用反斜杠分隔。 99 | 在Linux和macOS上,所有内容都属于根命名空间,因此所有路径都以斜杠或绝对路径开头。 100 | 在Windows上,每个分区都有一个根目录,因此您可能已经看到过类似C冒号反斜杠或D冒号反斜杠的内容。 101 | 因此,在Windows上,每个驱动器都有一个单独的文件系统路径层次结构,而在Linux和macOS上,所有这些都挂载在一个命名空间下。 102 | 您可能已经注意到我说了绝对路径这个词,您可能不知道它是什么意思。 103 | 绝对路径是完全确定文件位置的路径。 104 | 因此,在此示例中,它仅针对特定的echo文件,并提供了该文件的完整路径,但也有相对路径。 105 | 相对路径是相对于您当前所在的位置,我们找出当前位置的方法是可以键入PWD以获取当前工作目录。 106 | 现在,我在根目录下的home目录中,然后是John下的dev目录,等等。 107 | 从这里,我可以选择更改我的当前工作目录,所有相对路径都相对于当前工作目录,这基本上是您所在的位置。 108 | 在这个例子中,我可以输入CD /home。 109 | CD代表更改目录,这是我更改当前工作目录的方式。 110 | 在这种情况下,我更改到home目录,您会看到我的shell提示已更改以显示我现在在home目录中。 111 | 它只给我路径的最后一段的名称,但您还可以配置终端,让它在任何地方都给您完整的路径。 112 | 现在,如果我再次输入PWD,它会告诉我我在“/ home”中。 113 | 还有几个特殊的目录存在:“.”和“..”。 114 | “.”表示当前目录,“..”表示父目录。 115 | 所以这是一个可以轻松导航系统的方式。 116 | 例如,如果我输入CD“..”,它会告诉我我现在在“/”中,所以我现在在文件系统的根目录中。 117 | 我在“/home”中,现在我在“/”中,如果我输入PWD,它会做正确的事情。 118 | 然后,我还可以使用相对路径进入文件系统,对吧?所以我可以做CD“./home”,这将进入当前目录下的home目录,对吧?所以这会把我带回到“/home”。 119 | 如果我现在再试着CD“./home”,它会说没有这样的目录,因为当前目录下没有我要找的home目录,而我通过CD更改了当前目录。 120 | 我可以使用相对路径CD一直返回到我使用相对路径的地方,我也可以做类似“../../..”这样的事情,以回到我的文件系统的某个深处。 121 | 这恰好回到根目录,所以这里有一个“bin”目录,还有另一个“bin”,有一个“echo”文件,然后我可以做“echo world”,这将在“bin”下运行“echo”程序。 122 | 所以这是一种可以构建路径以任意遍历文件系统的方法。 123 | 有时您想使用绝对路径,有时您想使用相对路径。 124 | 通常,您希望使用最短的路径,但是如果您想,例如,运行一个程序或编写一个运行像“echo”或“date”这样的程序的程序,并且希望它可以从任何地方运行,则要么只需给出程序的名称,如“date”或“echo”,让shell使用路径来找到它在哪里,要么给出其绝对路径,因为如果您给出相对路径,则如果我在我的主目录中运行它,而您在其他目录中运行它,它可能对我有效,但对您无效。 125 | 通常,当我们运行程序时,它将至少默认地在当前工作目录上操作,除非我们给它任何其他参数。 126 | 这非常方便,因为它意味着我们通常不必为事物提供完整路径,我们只需使用文件名和当前所在的目录。 127 | 一个非常有用的技巧是找出当前目录中有哪些内容。 128 | 所以我们已经看到了PWD命令,它会打印出您当前所在的位置。 129 | 有一个命令叫做“LS”,它会显示当前目录中的文件列表。 130 | 所以如果我在这里输入LS,这是当前目录中的所有文件,对吧?这是一个快速浏览文件系统的方便方式。 131 | 你会发现如果我执行CD“。 132 | ”然后执行LS,它会显示那个目录中的文件。 133 | 但是对于LS,我也可以给它“LS ..”,就像我可以给它一个路径,然后它会LS那个文件而不是我当前所在的那个,或者LS那个目录。 134 | 你也可以看到这一点,如果我一直到根目录,对吧?根目录有不同的文件。 135 | 你可能不知道的一个方便技巧是有两个特殊的东西可以做。 136 | 一个是波浪号字符。 137 | 这个字符会将您带到您的主目录,所以波浪号总是扩展到主目录,您可以对其进行相对路径。 138 | 所以,我可以做波浪号斜杠dev斜杠P DOS classes missing semester,现在我在那里,因为波浪号扩展到斜杠home斜杠John。 139 | 特别是对于CD,还有一个非常方便的参数,就是“-”。 140 | 如果你做CD -,它会CD到你之前所在的目录。 141 | 所以,如果我做CD -,我回到了根目录。 142 | 如果我再做CD -,我回到了missing semester。 143 | 这是一个方便的方法,如果你想在两个不同的目录之间切换。 144 | 在LS或CD的情况下,可能有一些你不知道的参数。 145 | 现在,除了提供路径以外,我们还没有做任何事情,但你如何发现你可以首先给LS一个路径呢?大多数程序都采用所谓的参数,如标志和选项。 146 | 这些通常以“-”开头。 147 | 其中一个方便的选项是-help。 148 | 大多数程序都实现了这个选项,如果你运行,例如,LS-help,它会有用地打印出关于该命令的一堆信息。 149 | 你会看到这里说的用法是LS,你可以给它一些选项,也可以给它一些文件。 150 | 这个用法说明的方法是:三个点表示一个,像零个、一个或多个,而方括号表示可选项。 151 | 因此,在这种情况下,有可选的选项数量和可选的文件数量。 152 | 你会看到它说明了程序的功能,并指定了许多不同类型的标志和选项,你可以给出这些选项。 153 | 通常,我们称单个破折号和单个字母的东西为标志,不带值的东西为标志,带有值的东西为选项。 154 | 例如,“-a”和“-all”都是标志,“-C”或“-color R”是选项。 155 | 如果你向下滚动足够远,你会看到-L标志。 156 | 这没什么用。 157 | -L标志使用长列表格式。 158 | 现在,这本身并不特别有用,但让我们看看它实际上是做什么的。 159 | 所以,如果我输入LS -L,它仍然打印当前目录中的文件,但它给我更多关于这些文件的信息。 160 | 你会发现自己经常使用这个功能,因为它提供的额外信息通常非常有用。 161 | 让我们看看一些信息是什么。 162 | 首先,在某些条目的开头的D表示某些内容是一个目录。 163 | 因此,这里的“_data”条目是一个目录,而HTML不是目录,它是一个文件。 164 | 之后的字母表示为该文件设置的权限。 165 | 因此,正如我们之前所看到的,我可能无法打开特定的文件或无法进入特定的目录,这都由该特定文件或目录上的权限所决定。 166 | 阅读这些的方法是,前三个字符的第一组是为文件的所有者设置的权限。 167 | 你会看到,所有这些文件都是我拥有的。 168 | 第二组三个字符是为拥有该文件的组设置的权限。 169 | 在这种情况下,所有这些文件也都是由john组拥有的。 170 | 最后一组三个字符是其他人的权限列表。 171 | 所以,任何不是用户所有者或组所有者的人,这个目录可能有点无聊,因为所有的东西都是由我拥有的。 172 | 但如果我们做一些类似于CD slash和LS dash L的操作,你会看到这里所有的文件都是由root拥有的。 173 | 我们将回到什么是root用户,但在这里你会看到一些权限有点更有趣。 174 | 三个字符的组是读取、写入和执行。 175 | 这些对文件和目录的含义不同。 176 | 对于文件来说,它很简单。 177 | 如果你对一个文件有读取权限,那么你可以阅读它的内容。 178 | 如果你对一个文件有写入权限,那么你可以保存文件,添加更多内容或完全替换它。 179 | 如果你对文件有执行的X位,那么你被允许执行那个文件。 180 | 因此,如果我们在slash bin和user bin中进行LS al,你会看到所有的文件都有执行位设置,即使是不是文件所有者的人也是如此。 181 | 这是因为我们希望所有计算机上的人都能执行echo程序。 182 | 没有理由只允许某些用户运行echo。 183 | 这没有任何意义。 184 | 然而,对于目录,这些权限有点不同。 185 | 因此,读取转换为-你是否被允许看到这个目录中的哪些文件?因此,将读取视为目录的列表。 186 | 你是否被允许列出它的内容?对于目录的写入是指你是否被允许在该目录中重命名、创建或删除文件。 187 | 因此,它仍然有点正确,但请注意,这意味着如果你对一个文件有写入权限,但你没有对它的目录有写入权限,你就不能删除该文件。 188 | 你可以清空它,但你不能删除它,因为这需要写入到目录本身。 189 | 最后,目录的执行对许多人来说是一个容易出错的地方。 190 | 191 | 最后一组权限是每个人的权限列表。 192 | 所以,任何不是用户所有者或组所有者的人,这个目录可能会有点无聊,因为所有东西都是由我拥有的。 193 | 但是如果我们做一些像 CD 到根目录,然后做 LS -L,你会看到这里所有的都是由 root 拥有的。 194 | 我们会回到 root 用户是什么,但是在这里你会看到一些权限有点更有趣。 195 | 三个组是读、写和执行。 196 | 这些对文件和目录的含义不同。 197 | 对于文件来说,很简单。 198 | 如果您对文件具有读取权限,则可以读取其内容。 199 | 如果您对文件具有写入权限,则可以保存该文件,可以添加更多内容或完全替换它。 200 | 如果您对文件具有执行(X)位,则允许您执行该文件。 201 | 因此,如果我们在 /bin 中做 LS -al,那么在 novel 和 user bin 中,您会看到所有这些文件都设置了执行位,即使对于不是该文件所有者的人也是如此。 202 | 这是因为,例如 echo 程序,我们希望计算机上的每个人都能够执行。 203 | 没有理由只允许某些用户运行 echo。 204 | 这真的没有任何意义。 205 | 然而,对于目录,这些权限有些不同。 206 | 因此,读取权限是指您是否被允许查看该目录内部的文件。 207 | 因此,请将读取权限视为目录列表。 208 | 您是否被允许列出其内容?目录的写入权限是指您是否被允许在该目录中重命名、创建或删除文件。 209 | 因此,它仍然有些正确,但请注意,这意味着如果您对文件具有写入权限,但您没有对其目录具有写入权限,则无法删除该文件。 210 | 您可以清空它,但无法删除它,因为这将需要写入目录本身。 211 | 最后,目录上的执行是一些人经常遇到问题的东西。 212 | 目录上的执行是所谓的搜索,这并不是一个非常有用的名称,但是这意味着:如果您想要打开、读取或写入文件,如果要CD进入目录,您必须拥有该目录及其父目录上的所有父目录的执行权限。 213 | 214 | 例如,为了访问 /usr/bin 中的文件,例如 usr/bin/echo,我必须在根目录、用户目录和 bin 目录上都有执行权限。 215 | 如果我没有所有这些执行位,我将不被允许访问该文件,因为我将无法进入其中的目录。 216 | 您可能会遇到一些其他位,例如 S 或 T,您可能会看到 LS,如果您感兴趣,我们可以在办公时间里讨论它们。 217 | 它们大多数情况下对你在这个课程中所做的事情并不重要,但了解它们是很方便的。 218 | 所以,如果你对它们感兴趣,可以自己查阅或在办公时间来问我们。 219 | 现在还有一些其他有用的程序要知道。 220 | 哦,对不起,我之前提到过,如果只有一个横线,那就意味着你没有该权限。 221 | 所以,如果它说,例如,我们的横线X,这意味着你有读和执行权限,但没有写权限。 222 | 这个时候还有一些其他有用的程序要知道。 223 | 其中之一是move或MV命令。 224 | 所以,如果我CD回到missing semester,这里的MV命令让我重命名文件。 225 | 而重命名需要两个路径,它需要旧路径和新路径。 226 | 这意味着move命令既可以重命名文件(例如更改文件名但不更改目录),也可以将文件移动到完全不同的目录中。 227 | 你只需要给出当前文件的路径和你想要该文件的路径,就可以改变它的位置和名称。 228 | 例如,我可以将dot files dot MD移动到food MD不方便地位置,对吧?同样,我也可以将它移回来。 229 | 还有CP命令,即复制文件,非常类似。 230 | 它让你复制一个文件。 231 | CP也需要两个参数,一个是你想要从哪里复制,一个是你想要复制到哪里,这些都是完整的路径。 232 | 所以,例如,我可以使用这个命令来将点文件夹MD复制到上一级目录下的food MD,当然是food MD。 233 | 现在,如果我在当前目录下输入LS dot,你会看到那个目录下有一个叫做food MD的文件。 234 | 因此,CP也需要两个路径,它们不必在同一个目录下。 235 | 同样地,还有一个RM命令可以让你删除文件,并且可以给出路径。 236 | 在这种情况下,我删除了点点斜杠food。 237 | 需要注意的是,在Linux上,默认情况下,删除不会递归进行。 238 | 所以你不能使用RM删除一个目录。 239 | 你可以使用-our标志进行递归删除,然后给出你想要删除的路径,它将删除路径下的所有内容。 240 | 还有一个RM dr dir命令可以让你删除一个目录,但它只能删除空目录。 241 | 这里的想法是作为一个安全机制,以防止你不小心扔掉了一堆文件。 242 | 最后一个小命令很方便,那就是make,它可以让你创建一个新目录。 243 | 正如我们之前讨论的那样,你不应该这样做,因为它会为你创建两个目录,一个叫做“my”,另一个叫做“photos”。 244 | 如果你真的想创建这样一个目录,你应该使用反斜杠转义空格或者用引号引起来。 245 | 如果你想要更多关于任何命令在这些平台上如何工作的信息,那么还有一个非常方便的命令。 246 | 这个命令叫做man,用于手册页。 247 | 这个程序以另一个程序的名称作为参数,并给出它的手册页。 248 | 因此,例如,我们可以使用man LS来查看LS的手册页。 249 | 你会注意到,在LS的情况下,它与LS -help得到的信息相当相似,但更容易浏览、更容易阅读。 250 | 通常在底部,你还会得到示例、编写者信息、更多信息的位置等信息。 251 | 有一件有时可能会令人困惑的事情,直到最近的一个版本加入了底部的“按 Q 退出”的三个字,它们以前没有这么说。 252 | 你按 Q 键退出该程序。 253 | 如果你不知道这个便捷的键盘快捷方式,退出它可能会很困难。 254 | 顺便说一下,Ctrl+L 是一个便捷的键盘快捷方式,它可以清除终端并回到顶部。 255 | 到目前为止,我们只谈到了单独使用程序,但是当你开始组合不同的程序时,shell 的大部分强大功能真正体现出来了。 256 | 你可能希望将多个程序链接在一起,而不是仅仅运行 CDE,运行 LS 等等。 257 | 你可能希望与文件交互,并且让文件在程序之间操作。 258 | 我们可以使用 shell 给我们的流的概念来实现这一点。 259 | 每个程序默认有两个主要的流,我会简单地说,它有一个输入流和一个输出流。 260 | 默认情况下,输入流是你的键盘,基本上是你的终端,你在终端中输入的任何内容都会进入程序,它有一个默认的输出流,也就是每当程序打印出什么东西时,它将打印到那个流中,默认情况下也是你的终端。 261 | 这就是为什么当我输入 echo hello 时,它会被打印回我的终端,但是 shell 给了你一种方法来重定向这些流,以改变程序的输入/输出指向。 262 | 最简单的方法是使用尖括号符号。 263 | 因此,你可以像这样写:“echo hello > hello.txt”,或者像这样写:“cat < hello.txt > hello2.txt”。 264 | 左尖括号表示将这个程序的输入重定向为这个文件的内容,右尖括号表示将前面程序的输出重定向到这个文件中。 265 | 所以让我们看一个例子。 266 | 如果我输入“echo hello > hello.txt”,我可以说我希望这个内容存储在一个名为 hello.txt 的文件中,因为我给出的是相对路径,这将在当前目录中构建一个名为 hello.txt 的文件,至少在理论上,它的内容应该是单词“hello”。 267 | 如果我运行这个命令,你会注意到没有任何东西打印到我的输出中。 268 | 之前当我运行 echo hello 时,它打印出了 hello。 269 | 现在“hello”已经进入一个名为 hello.txt 的文件,我可以使用叫做 cat 的程序来验证这一点。 270 | "cat" 是一个打印文件内容的命令。 271 | 比如,我可以输入命令 "cat hello.txt",它就会显示出 "hello"。 272 | 不过 "cat" 还有另一种功能,支持将输入与输出连在一起。 273 | 比如,我可以输入命令 "cat",默认情况下它只是将输入内容复制到输出中,但我可以告诉它从文件 "hello.txt" 中读取输入。 274 | 此时,shell 会打开文件 "hello.txt",读取其中的内容并将其设置为 "cat" 命令的输入,"cat" 将其输出,因为我没有将输出重定向,所以输出将会显示在终端上。 275 | 这样,"hello" 就会被打印到输出中。 276 | 我也可以同时使用这两种方法。 277 | 比如,如果我想复制一个文件,但不想使用 "cp" 命令,我可以使用 "cat" 命令。 278 | 在这种情况下,我什么也没告诉 "cat" 命令,只是让它按照默认方式运行。 279 | "cat" 命令并不知道这个重定向操作,但是我告诉 shell 使用 "hello.txt" 作为 "cat" 命令的输入,并将 "cat" 命令打印的任何内容写入 "hello2.txt"。 280 | 同样,这并不会将任何内容打印到我的终端上。 281 | 但是,如果我输入 "cat hello.txt",就会得到我预期的输出,即原始文件的副本。 282 | 还有一种双向箭头,它是追加而不是覆盖,所以如果我执行 "cat hello.txt >> hello2.txt",然后执行 "cat hello2.txt",它仍然只包含 "hello",即使它已经包含了 "hello"。 283 | 如果我把它改成双向箭头,就表示追加。 284 | 现在,如果我查看该文件,它就包含了两个 "hello"。 285 | 这些命令通常只是与文件交互的简单方法,但当涉及到 shell 提供的另一个运算符,即管道符时,它们就变得更加有趣了。 286 | 管道符就是一根竖杠,它的作用是将左边程序的输出作为右边程序的输入。 287 | 比如,以 "ls /" 或 "ls -l /" 为例,它们会输出一些东西。 288 | 如果我只想得到输出的最后一行,可以使用一个叫做 "tail" 的命令,它可以打印输入的最后 n 行,我可以加上 "-n1" 参数,这表示只打印最后一行。 289 | 我可以将这两个命令连在一起,比如输入命令 "ls -l / | tail -n1",注意到这里的 "ls" 不知道 "tail","tail" 也不知道 "ls". 290 | 它们是不同的程序,从未被编程为彼此兼容。 291 | 它们只知道如何从输入读取并写入输出,然后管道将它们连接在一起,在这种情况下,我说我希望“ls”的输出成为“tail”的输入,然后我希望“tail”的输出仅流到我的终端,因为我没有重定向它。 292 | 我也可以重新连接它,以便输出到“ls.txt”,在这种情况下,如果我“cat ls.txt”,我将获得适当的输出。 293 | 事实证明,你可以用这个做一些非常有趣的事情。 294 | 我们将在数据整理讲座中详细介绍这个问题,在像四天后的高级管道中会讲述一些高级内容。 295 | 比如,我们可以做类似于“curl --head --silent google.com”的东西,这样就可以给我所有访问google.com的HTTP头,然后我可以将其管道传输到“grep -a”(如“-A --ignore-case”或者如果我想要内容长度的话,就只用“-i”),这样就会打印内容长度的头文件。 296 | “grep”是一个程序,我们稍后会谈到,它可以让你在输入流中搜索指定的关键字。 297 | 我们可以将其通过“cut”命令进行处理,该命令需要将分隔符设置为空格,并且我只需要第二个字段,这样就只打印了内容长度。 298 | 这个例子有点愚蠢,对吧?就像这只是让你从命令行中提取google.com的字节数,这不是一个非常有用的事情,但你可以看到通过将它们链接在一起,你可以实现一堆真正有趣的文本操作效果。 299 | 事实证明,管道不仅适用于文本数据,你也可以对图像等东西使用它们。 300 | 你可以有一个程序,用于处理其输入上的二进制图像,并将二进制图像写入其输出,然后可以以这种方式将它们链接在一起。 301 | 我们将在以后讨论一些这种类型的例子-甚至可以用于视频。 302 | 你可以进行数据流传输。 303 | 比如说,如果你家里有 Chromecast,你可以通过在管道中使用最后一个程序作为 Chromecast 发送程序的方式来进行视频文件的流传输。 304 | 你将视频文件流传输到它中,它会将视频文件以流或 HTTP 的形式传输到你的 Chromecast 上。 305 | 我们将在数据整理课程中更详细地讲解这个。 306 | 但是还有一件事情,我想和你讨论一下,关于如何以更有趣、更强大的方式使用终端,这也许会对那些已经对终端感到舒适的人来说很有趣。 307 | 但首先,我们需要讨论 Linux 系统和 macOS 系统中的一个重要主题,即 root 用户的概念。 308 | root 用户有点像 Windows 上的管理员用户,它的用户 ID 是零。 309 | root 用户是特殊的,因为它可以在你的系统上随意做任何事情,即使某个文件对任何人都不可读或不可写,root 仍然可以访问该文件。 310 | root 是一个超级用户,可以做任何想做的事情,但大多数时间,你不会作为超级用户来操作。 311 | 你不会成为 root 用户。 312 | 你会像John或者其他你的名字一样成为一个用户,这将是你所使用的用户,因为如果你一直以root用户的身份操作计算机,如果运行了错误的程序,它们可能会彻底破坏你的计算机,你肯定不希望发生这种情况,对吧?但偶尔你需要做一些需要使用root权限的事情。 313 | 通常情况下,你会使用一个叫做sudo的程序。 314 | sudo或do as su,这里的su指的是超级用户,因此这是一种以超级用户的方式执行以下操作的方法。 315 | 通常,sudo的工作方式是你像在终端上一样写入sudo和一个命令,它将以root用户的身份运行该命令,而不是你实际使用的用户身份。 316 | 在哪些情况下可能需要使用这样的东西呢?嗯,有许多特殊的文件系统在你的计算机上,但特别的,有一个叫做sysfs的文件系统。 317 | 如果你CD到斜杠sys,这是一个全新的世界。 318 | 这个文件系统实际上不是你计算机上的文件,而是各种内核参数。 319 | 内核就像是你的计算机的核心。 320 | 这是一种通过看起来像文件系统的方式访问各种内核参数的方法。 321 | 你会看到,如果我进入class目录,例如,它有许多不同类型的设备可以与之交互,或者可以访问各种队列或各种内部奇怪的旋钮。 322 | 由于它们暴露为文件,这意味着我们可以使用到目前为止使用的所有工具来操作它们。 323 | 其中一个例子是,如果你进入sys/class/backlight,这会直接让你配置笔记本电脑的背光灯(如果你有的话)。 324 | 所以我可以进入Intel backlight,这是一台Intel笔记本电脑。 325 | 在这里面,你会看到有一个叫做brightness的文件,我可以cat这个文件,这是屏幕当前的亮度。 326 | 但不仅如此,我还可以修改它以改变屏幕的亮度。 327 | 所以你可能会认为我可以做一些像“echo让我们把它做一半或者什么的,echo 500到亮度”这样的事情。 328 | 如果我这样做,它会说“权限被拒绝”。 329 | 我无法修改亮度,因为要基本更改内核中的东西,您需要成为管理员。 330 | 您可能会想到解决方法是编写“sudo echo 500”,但我仍然会收到权限被拒绝的错误。 331 | 为什么呢?因为正如我之前提到的那样,输入和输出的重定向不是程序所知道的。 332 | 当我们将“LS”管道传输到“tail”时,“tail”并不知道“LS”,而“LS”也不知道“tail”。 333 | 这个管道和重定向是由shell设置的。 334 | 所以,在这种情况下,我告诉我的shell运行程序“sudo”并使用参数“echo”和“500”,并将其输出发送到名为“brightness”的文件,但是shell是打开brightness文件的东西,不是“sudo”程序。 335 | 所以,在这种情况下,作为我运行的shell尝试打开brightness文件进行写入,但是不允许这样做。 336 | 因此,我会收到权限被拒绝的错误。 337 | 如果您搜索某些内容,最终进入Stack Overflow并告诉您只需运行此命令即可看到它的效果,您可能已经看到了这个错误,例如1、2、sis,例如“net ipv4 forward”。 338 | 如果您正在设置防火墙,此命令应该可以正常工作,因为这个小井号符号表示以root身份运行此命令。 339 | 这是很少被解释的内容,但这就是井号符号的含义。 340 | 您可以看到我的提示符上有一个美元符号,而美元符号表示您不是以root身份运行。 341 | 那么问题是如何解决这个问题?好吧,我可以切换到root终端,因此可以通过运行“sudo su”来执行此操作。 342 | “sudo su”表示以root身份运行以下命令,“su”是一个复杂的命令,可以让您有效地以超级用户身份获取一个shell。 343 | 所以,如果我这样做,输入我的密码,那么现在您会看到用户名从“jon”变为“root”,而提示符从美元符号变为井号符号。 344 | 如果我现在进入该文件,如果我执行“echo 500到brightness”,我的屏幕变得有点暗,但您看不到它,您只需相信我,现在我没有收到错误。 345 | 这是因为shell现在正在以root身份运行。 346 | 它不是以Jon的身份运行,而是以root用户的身份运行,root用户允许打开此文件。 347 | 但是,根据我们现在对终端的了解,实际上有一种方法可以在不必降到root shell的情况下完成,方法如下... 我猜是恢复到1060。 348 | 你看出这个方法和之前的不同了吗?这里,我告诉我的终端运行“echo 1060”命令,它会打印出1060,然后我告诉它运行“sudo tee brightness”命令,并让它将“echo”的输出发送到“sudo tee”中。 349 | 要理解这个过程,你需要知道tee命令的作用。 350 | tee命令会将输入内容写入一个文件,同时也将它输出到标准输出。 351 | 因此,如果你想将一个日志文件保存起来以备之后查看,但同时也想在屏幕上看到它,你就可以将其通过tee命令管道传输,给它一个文件名,它就会将输入的内容同时写入那个文件和你的屏幕上。 352 | 在这里,我利用了这个命令,让sudo tee作为root用户运行,并将输出写入brightness文件中。 353 | 因此,在这种情况下,打开brightness文件的程序sudo tee是作为root用户运行的,所以它是被允许的。 354 | 如果我运行这个命令,现在屏幕的亮度已经变亮了,虽然你看不到,而且我也没有收到任何错误消息,我也不需要切换到root shell并在那里运行命令,因为这样做通常是有一定危险的。 355 | 如果你想更深入地了解这个文件系统,你可以在里面浏览,你会发现许多有趣的东西。 356 | 例如,我们发现这里有一个有趣的brightness命令。 357 | 我想知道我还能设置什么其他的亮度。 358 | 因此,我可以使用find命令,我们也将在接下来的课程中讲解。 359 | 我让它在当前目录中寻找文件名中包含“brightness”的任何文件。 360 | 嗯,这样有点不太有用。 361 | 也许它们不是文件。 362 | 我拼写错了吗?好吧,幸运的是,我已经知道一个很方便的方法,那就是有一个名为LED的子目录,而LED也有亮度设置。 363 | 有哪些LED呢?哦,有很多东西,例如滚动锁定LED。 364 | 现在,大多数人可能不知道滚动锁定LED是什么,更不用说滚动锁定键了。 365 | 你可能已经在键盘上看到过一个名为滚动锁定的键,基本上没有人知道它的意义,也没有人真正使用它做任何事情。 366 | 它基本上只是一个死键和一个死LED。 367 | 如果你想配置它,使得每次你收到电子邮件时,滚动锁定LED都会亮起来,因为没有其他原因它会亮起来,那该怎么办呢?如果我们进入到这个具有亮度设置的特定目录,并将其设置为零,那么如果我写入一个一会发生什么呢?你可能不应该随意在此目录中的随机文件中写入随机数字,因为你会直接影响到你的内核。 368 | 了解这些文件的功能。 369 | 在这种情况下,我已经准备好了安全眼镜,并做了我的研究,所以现在你看不到,但在我的键盘上,滚动锁定LED现在是亮着的。 370 | 因此,如果我编写了一个程序来检查邮件等内容,并在结束时运行一个将一个写入该文件的程序,那么我现在就有了一种方法,可以让我键盘上的LED指示我是否有新的电子邮件。 371 | 此时,你应该大致了解如何在终端和shell中操作,并足够了解这些基本任务的原理。 372 | 现在,你不应该再需要使用点和单击接口来查找文件了。 373 | 你可能需要掌握一个剩余的技巧,那就是打开文件的能力。 374 | 到目前为止,我只告诉了你如何查找文件。 375 | 但你应该知道的一件事是Missing Semester:xdg-open。 376 | 这可能仅适用于Linux。 377 | 在Mac OS上,我认为它只被称为“打开”。 378 | 在Windows上,谁知道呢?xdg-open,你可以给出一个文件名,它将在适当的程序中打开它。 379 | 因此,如果你使用xdg-open打开一个HTML文件,它将打开你的浏览器并打开该文件。 380 | 一旦你有了这个程序,在理论上,你应该不再需要打开Finder窗口了。 381 | 你可能会因其他原因想要打开Finder窗口,但在理论上,你可以使用我们今天学习的工具完成所有操作。 382 | 对于你们中的一些人来说,这可能都很基础,但正如我所提到的,这是我们现在知道shell如何工作的上升期,而我们将在未来的讲座中大量使用这些知识,使用shell做真正有趣的事情。 383 | 这是我们要使用的接口,所以我们都要了解它。 384 | 我们下一节课会更多地讨论如何自动化这样的任务,如何编写脚本来运行一堆程序,如何在终端中处理条件和循环等等,并运行一个程序直到它失败,这在你想运行一些测试套件直到它失败的情况下可能会很方便。 385 | 那就是下周的讲座主题。 386 | 你有问题吗?你一直在演示的是这个“assist”目录,假定只有在你运行...这是个好问题。 387 | 我不知道Windows子系统是否会暴露sys文件系统。 388 | 如果它这样做了,它可能只暴露了很少一部分。 389 | 可能是因为有...我不知道,你可以查一下。 390 | 你会发现,本讲座的讲义已经在线上了,在文件的最底部有一些练习。 391 | 其中一些相对容易,一些可能有点难,我们鼓励你尝试一下。 392 | 如果你已经知道这些东西,应该会很快的。 393 | 如果你不知道,它可能会教你很多你可能不知道的东西。 394 | 对于我们在讲座后要进行的办公时间,我们将乐意帮助你完成所有这些,或者如果在此过程中学习了其他命令,你想更有效地使用它们,我们也会很乐意帮助你。 395 | 然后在下一节课中,我们将假定您已经知道练习将要教给您的内容。 396 | 网站上也有一个电子邮件地址,您可以发送问题,如果在办公时间结束后想到了问题。 397 | 结束前还有问题吗?没有?好的,我们将在盖茨大楼九楼,32号楼举行办公时间,大约五分钟后开始。 398 | 好的,到时候见。 399 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture03_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 好的,很棒。 2 | 欢迎来到“缺失的学期”第三堂课。 3 | 今天我们要讲述的是文本编辑器。 4 | 我非常喜欢这个话题。 5 | 我认为这是我们在这门课程中教授的最有价值的话题之一,因为作为程序员,你们花费了大量的时间来编辑文本和程序,如果你们能够投入时间学习如何更高效地编辑,那么你们将会节省大量的时间。也许在你们的本科阶段或职业生涯中,能够节省数百个小时。 6 | 文本编辑器与其他编辑程序有所不同,例如用于编辑英语散文的程序,因为编程与撰写英语散文不同。 7 | 在编程时,你需要花费大量的时间阅读你所编写的内容,花费大量的时间在缓冲区中导航,并花费大量的时间进行小的编辑,而不是像写文章时那样连续写作。 8 | 因此,用于这些不同目的的程序也不同,对吧?比如说,用于写文章的 Microsoft Word 和用于写代码的 Vim、Emacs、VS Code 和 Sublime 等等。 9 | 学习文本编辑器并成为熟练使用者的方法是从教程开始学习。这基本上就是今天讲座的内容,再加上我们为你们提供的练习。 10 | 然后,在教程之后,你需要使用编辑器来完成所有的编辑任务。 11 | 当你学习一个复杂的工具时,比如今天我们要教你 Vim,这是很多程序员使用的一个强大的编辑器,可能最初使用这个工具会使你在编程时变慢一点。但请坚持下去,因为我保证,大约使用新的编辑器编程20个小时后,你会重新达到使用旧工具编程的速度。之后,你会开始受益,随着你的不断学习,你会变得越来越快。 12 | 对于像 Vim 这样复杂的程序,学习基础并不需要太长时间,但需要一辈子去掌握。 13 | 因此,在你使用这个工具的整个过程中,请确保你随时查询相关资料来帮助你进行编辑。 14 | 15 | 如果你遇到这样的情况:“哦,这种做事情的方式真的很低效,有更好的方式吗?”答案几乎总是肯定的。因为这些文本编辑器是由程序员为程序员编写的,所以当然,像编写这些工具的人一样,他们遇到了相同的问题并解决了它们,这样你就不必再去处理这些问题了。 16 | 因此,在你学习的过程中,确保你查阅资料时要注意及时搜索。你可以使用谷歌或随时给我们发送电子邮件。如果有问题,你可以参加办公时间,我们会帮助你快速地解决问题。 17 | 至于要学习哪种编辑器,在之前的几个版本中,我们实际上避免教授特定的编辑器,因为我们不想把我们的观点强加给大家。但我们认为教你如何使用一种特定的工具并善加利用它非常有用。 18 | 因此,人们对编辑器有着非常强烈的观点,所以你可以在课程笔记中查看更多关于这个主题的链接。 19 | 回顾这些年来哪些编辑器比较受欢迎,我相信你们都听说过 Stack Overflow。他们每年都会进行一次调查,询问开发人员各种问题,其中一个问题是你使用哪种文本编辑器。目前最受欢迎的图形编辑器似乎是 VS Code,而基于命令行接口的最受欢迎的编辑器是 Vim。 20 | 因此,我们将会教授你 Vim,这样做有几个原因。 21 | 其一是我们所有的讲师,包括我、John和Jose,都将 Vim 作为我们的主要编辑器使用。我们使用它已经很多年了,也非常满意。 22 | 我们认为它背后有很多有趣的思想,因此即使你最终不会使用这个特定的工具,学习这些思想也是有价值的。 23 | 此外,许多工具都非常兴奋地关注 Vim 中的思想,并支持 Vim 模拟模式。例如,今天最流行的编辑器 VS Code 支持 Vim 绑定。 24 | 25 | 截至目前为止,Vim仿真模式已经有大约140万次下载。 26 | 在本次讲座中,你会发现许多不同的工具都支持Vim仿真模式,包括你的shell、Python REPL和Jupyter notebook等。甚至你的网络浏览器也可以支持Vim仿真模式。 27 | 因此,我们今天将向你介绍这个非常棒的工具,并在本讲座中教授你Vim的核心哲学、一些基础内容(如如何打开、关闭、导航和编辑文件)以及背后真正棒的想法。我们无法涵盖所有内容,因为Vim是一个非常强大的工具。 28 | 你可能无法记住本次讲座中的每一个小细节,因为我们会快速地浏览一些材料。但是所有的内容都在讲义中,而且练习还会为你提供一些教程和链接。因此,我强烈建议你至少完成所有非高级练习。 29 | 有问题吗?太好了。 30 | 好的,Vim的一个非常酷的想法是它是一个模态编辑器。这意味着Vim有多个操作模式。"模态"源于"mode"这个词。 31 | 这种想法的发展源于编程时经常需要处理不同类型的任务。有时你需要阅读代码,有时需要对代码进行小修改(例如找到某个位置并更改某个小细节),有时你需要一口气写很多代码(例如从头开始写一个函数)。因此,有不同的操作模式来处理这些不同类型的任务。 32 | 因此,我会在黑板上写下这个内容,这样我以后可以很方便地参考。当你启动Vim时,它会进入所谓的正常模式。在正常模式下,你可以使用不同的命令来导航和编辑文件。在其他模式下,Vim的行为会发生改变,例如在插入模式下,你可以像在其他文本编辑器中一样直接输入文本。 33 | 34 | 在这种模式下,所有不同的键组合都会以一种方式进行操作。 35 | 然后有不同的键组合可以将您从正常模式切换到其他模式,这些模式会更改不同键的含义。 36 | 因此,在大多数情况下,您将会在正常模式或所谓的插入模式下花费大部分时间。 37 | 要进入插入模式,您需要按下“i”键,要从插入模式返回正常模式,您需要按下“Escape”键。 38 | 关于符号的一些说明,因为我们以后会用到。 39 | 在我在本课程中使用的符号以及Vim用于给您反馈的符号中,有几种不同的方式来谈论不同的键。 40 | 因此,当他们谈论裸键时,比如键盘上的“i”键,他们会说'i'。 41 | 但是对于不同的键组合,比如当您按下控制键和某个键时,比如说,控制键V,它有大约三种表示方法。 42 | 其中一种表示方式是插入符号,然后是控制字符。 43 | 所以这就是控制键V。 44 | 另一种可能会写成(我认为我们在课堂笔记中是这样写的),是控制键v;这可能是您更常见的方式。 45 | 在其中的一些部分,这是以角括号C - V close angle bracket的形式写出的。 46 | 所以这是一些以后有用的符号表示。 47 | 所以,Vim有几种不同的模式,其中正常模式是用于在文件中导航,阅读内容,从文件到文件等。 48 | 然后插入模式是您键入文本的地方。 49 | 因此,在此处按下的大多数键将进入文本缓冲区,而在正常模式下按下的键不会放入缓冲区,而是用于导航或进行编辑。 50 | 实际上,情况要复杂得多。 51 | 还有许多其他模式,我将在此处写下它们,因为以后我们会参考它们。 52 | 因此,Vim还有一种替换模式,用于覆盖文本,而不是插入文本并将其推到前面。 53 | 然后,它具有许多不同的选择模式,因此它具有称为可视模式的模式,还具有可视行和可视块。 54 | 55 | 56 | 这个输入方式使用按下键盘上的 R 键;这个使用按下键盘上的 V 键;这个使用 Shift+B 输入;而这个则是通过按下 Control-V 输入。 57 | 此外,还有一个命令行模式,可以通过按冒号键进入。 58 | 好的,现在我们已经将它们写在板子上,以便以后参考。接下来我们可以试着实践一下了。 59 | 我们需要注意的一点是,从正常模式转到其他任何模式都需要按下某个键,但是从其他任何模式返回我们大部分时间要花在的正常模式时,我们需要使用键盘上的 Escape 键。 60 | 因此,在使用 Vim 时,经常需要按下 Escape 键。为了避免用左手小指去按键盘角落的小 Escape 键非常不方便,很多程序员会重新绑定键盘上的某个键为 Escape 键。因此,许多人使用大写锁定键,因为它就在主键盘区。我们在讲义中提供了如何进行按键重绑定的链接。 61 | 好的,既然我们已经讨论了 Vim 的核心思想之一——模态编辑的思想,我们可以讨论一些基础知识,比如如何打开这个文本编辑器,如何打开和保存文件等。 62 | 因此,这是一个基于命令行的程序,虽然也有一些图形化的变体,但是你启动这个程序的方式是运行 Vim。 63 | 你可能会注意到,在我的屏幕左下角,你实际上可以看到我刚才输入的内容。 64 | 在这节课中,当我输入 Vim 命令时,我会说出我输入的内容,但你也可以在屏幕上看到它。 65 | 66 | 所以,如果我按下Ctrl+C键,你会看到那边显示Ctrl+C。 67 | 这段字大家都能读清楚吗?太好了,那么我们打开Vim的方法就是在命令行中运行vim程序,大多数系统都预装了它。 68 | 如果你没有它,你可以使用你的包管理器进行安装。 69 | 如果我们想要用它来编辑特定的文件而不仅仅是打开程序,Vim也可以接受一个参数。 70 | 例如,我有一个文件在这个目录中;这实际上是这个讲座的讲义。 71 | 所以,我可以输入"vim editors MD",按ENTER,然后瞬间就开始了。 72 | 在这个讲座中,我没有在完全的Exton中运行Vim。 73 | 我配置了一些默认行为比较好的东西,比如在左边有行号,或者在底部有更多的状态信息等等。 74 | 如果你想使用这个默认配置开始,我们在讲义中有一个链接,所以你可以默认得到一个稍微更加合理的配置。 75 | 那么,一旦你打开了Vim,你要怎么做呢?嗯,就像我之前说的那样,Vim开始在normal模式下。 76 | 所以,如果我开始打字,比如输入“X”,它不会将“X”插入到缓冲区中。 77 | 你可以看到左上角的光标:它实际上删除了一个字符。 78 | 那是因为我在normal模式下,而不是insert模式。 79 | 所以,insert模式基本上是你过去使用过的所有其他文本编辑器所熟悉的,其中有一个光标,你按下字符,它就会进入到你的缓冲区。 80 | 在Vim中,你开始是在normal模式下,你可以按“I”键进入insert模式。 81 | 所以,我按下了“I”,然后注意到底部左侧的提示说“--INSERT”。 82 | 底部左侧总是告诉你你现在处于哪个模式下,除了normal模式,此时是空白的。 83 | 现在,我处于insert模式下,如果我按下“X”字符,它就会被插入到我的文本缓冲区中,我可以退格删除它,输入其他内容,现在我的文本编辑器就像你预期的任何其他程序一样运行。 84 | 85 | 从这个点开始,如果我想停止插入字符,我该如何回到正常模式呢?是的,确切地说,我按“escape”键。 86 | 这就是我的按键可视化器用于转义的符号,所以请注意这一点。 87 | Vim 认为使用鼠标效率低下。 88 | 就像你的手在键盘上移动到鼠标上需要很多时间一样,你不想在编程时浪费那几毫秒的时间,对吧?因此,所有 Vim 功能都可以通过键盘访问。 89 | 它可以像打开文件、保存文件等你可能习惯的方式进行操作,也可以通过键盘访问。 90 | 如何实现呢?通过其中的一个 Vim 模式,即命令行模式。 91 | 因此,如果你处于正常模式,并按下“:”键,你会注意到光标跳转到左下角,并显示我刚才输入的“:”符号。 92 | 现在,我可以输入命令。 93 | 你可以将其想象为我们过去几天谈论过的命令 shell,除了这是 Vim 的命令 shell,所以你在这里给 Vim 发送命令,而不是 shell 命令。 94 | 有很多内置命令可以完成你所需的所有任务,例如你可能想了解的一个命令是如何退出编辑器。 95 | 如果你处于正常模式,我可以按“Escape”键从命令行模式返回到正常模式,而且与许多程序不同的是,我按“Control+C”并不能退出 Vim。 96 | 那么我该如何退出 Vim 呢?我可以按“:”,然后进入命令行模式,然后我可以输入命令“quit”。 97 | “Q-U-I-T”。 98 | 你会看到我 - 也许我应该把这个移到中间或者其他地方 - 看到了“:quit”,然后按 ENTER,就退出 Vim 了。 99 | 100 | 我可以再次打开 Vim。 101 | 其实这个命令有一个缩写形式,只需要输入“:q”,就可以达到同样的效果。 102 | 还有很多类似这样有用的命令。 103 | 所以,还有一些其他方便的命令需要知道,比如如何保存文件?假设我在这里进行了一些编辑,比如“hello world”。 104 | 所以,我按下“i”进入插入模式-或者让我重做一遍-我按下“i”进入插入模式。 105 | 现在,我可以使用向下箭头...我觉得我稍微需要修改一下。 106 | 约翰,你可以修复一下配置吗?不用管那个。 107 | 好的,假设我跳到这一行,然后按下“i”进入插入模式,输入一些文本,然后按下“escape”回到正常模式。 108 | 那么,我该如何保存这个文件呢?嗯,这里有另一个命令。 109 | 所以,输入“:”进入命令模式,然后我可以输入“W”...然后按“Enter”。 110 | “W”代表写入。 111 | 底部显示“编辑器.md”等等,意思是文件已经保存,所以现在,如果我输入“:q”退出并重新打开同一个文件,你会看到更改已经被保存了。 112 | 还有一些其他命令...所以,有很多不同的 Vim 命令,因为不同的原因非常有用。 113 | 114 | 但是我现在会再向你解释几个命令。 115 | 一个非常有用的命令是“help”。 116 | “:help”,你可以使用它来查找特定键或命令的帮助文档。 117 | 所以,如果我想知道“:w”命令的作用,我可以使用“:help :w”,这会给我带来“:w”或“:write”的文档。 118 | 如果我执行“:q”,它会关闭这个窗口并把我带回到之前的位置。 119 | 请注意,“:help :w”和“:help w”是不同的,因为W键是正常模式下按下W键,这里是不需要加“:”的。 120 | 如果我查找“:w”的帮助,那就是W命令的帮助。 121 | 现在,你已经掌握了基本的操作方法,对吧?你可以打开编辑器,编辑一个特定的文件,按“i”键进入插入模式并输入一些文本,按“esc”键回到正常模式,然后“:w”保存更改,“:q”退出。 122 | 所以,你已经掌握了使用Vim编辑文件的基本技能,尽管有些效率低下。 123 | 到目前为止有问题吗?是的,在后面。 124 | 问题是,正常模式有什么好处?我们将在五分钟内更详细地讨论这个问题。 125 | 但简单来说,插入模式只用于输入文本。 126 | 当我编程时,我实际上花了很多时间移动我的文件并进行小的修改。 127 | 所以我来到这里,想把这个HTTPS链接改成HTTP链接。 128 | 我可以在正常模式下进行小的编辑和修改,我们将在五分钟内看到更多这样的内容。 129 | 好问题!还有其他问题吗?好的,继续讲,还有一件事情有点有用,我认为,在高层次上,Vim的缓冲区模型与窗口模型和选项卡模型。 130 | 131 | 那么,你之前使用的编辑器,比如Sublime Text或VS Code,你肯定可以在它们中打开多个文件,并可能有多个标签页和多个窗口。 132 | Vim也有这些不同的概念,但它的模式与大多数其他程序有些不同。 133 | Vim维护了一组打开的缓冲区——这是它用于打开文件的术语,它有一组打开的文件。 134 | 然后,与此分开,您可以有多个标签页,每个标签页可以有窗口。 135 | 这使得Vim与您之前使用的程序略有不同,因为缓冲区和窗口之间不一定是一对一的对应关系。 136 | 例如,我可以在这里做一件事——稍后我们将向您展示键组合和其他内容——创建两个不同的窗口。 137 | 我在上面有一个窗口,然后在下面有一个窗口。 138 | 请注意,同样的文件在两个窗口中都打开。 139 | 因此,如果我在这里进行一些编辑,它们实际上也会在下面的窗口中发生,因为两个窗口中打开的是同一个缓冲区。 140 | 这对于同时查看文件的两个不同部分非常有用。 141 | 所以你想能够在文件的顶部查看程序的导入,同时在下面工作的其他地方。 142 | 所以这是一个有用的东西需要记住,Vim有这种想法——有几个标签页,每个标签页有一些窗口,然后每个窗口对应一个缓冲区。 143 | 但是,一个特定的缓冲区可以在零个或多个窗口中同时打开。 144 | 这是我最初学习Vim时困惑的一件事,所以我想早些解释一下。 145 | 然后,我们之前提到的“:q”命令不完全是退出。 146 | 它有点像“关闭当前窗口”,然后当没有更多的打开窗口时,Vim才会退出。 147 | 148 | 所以,如果我在这里输入“:q”,它只会关闭上面的窗口,因为那是我所在的窗口,现在剩下的窗口变成了全屏模式。 149 | 我可以再次输入:Q来关闭这个窗口。 150 | 现在我们在我打开的第二个选项卡中。 151 | 如果我再次输入:Q,好的,现在Vim退出了。 152 | 如果你不想一遍一遍地按“:q”,你可以输入“:qa”来关闭所有打开的窗口。 153 | 好的,回答你的“正常模式究竟是用来干什么的”问题。 154 | 这是Vim中另一个非常酷的想法,我认为这实际上是这个程序最基本和有趣的想法。 155 | 就是像你们都是程序员一样,Vim有这样一种想法,就是Vim的正常模式、Vim的界面本身就是一种编程语言。 156 | 让我重复一下,这是一种非常有趣的想法:界面就是一种编程语言。 157 | 这是什么意思?这意味着不同的按键组合有不同的效果,一旦你学会了不同的效果,你就可以将它们结合在一起——就像在编程语言中一样——你可以学习不同的函数等等,然后将它们粘合在一起,制作一个有趣的程序。 158 | 同样,一旦你学会了Vim的不同的移动和编辑命令等等,你就可以通过编程Vim来与Vim交流。 159 | 一旦这变成了肌肉记忆,你就可以以你思考的速度编辑文件。 160 | 对于我来说,我不认为我能够用过去使用的任何其他文本编辑器做到这一点,但这个编辑器非常接近。 161 | 那么,让我们深入了解正常模式的工作原理。 162 | 你可以尝试跟着这个进行操作,打开Vim中的一些随机文件,并跟随我键入的一些键组合。 163 | 164 | 所以,一个基本的事情是你可能想做的,就是在文本中浏览,移动你的光标上下左右。 165 | 在 Vim 中,你可以使用 hjkl 键来实现,而不是箭头键。 166 | 虽然默认情况下箭头键也可以工作,但尽量避免使用它们,因为你不想一直将手移动到箭头键,这浪费了很多时间。 167 | 而 hjkl 键在键盘的主键区上,J 键向下移动,K 键向上移动,H 键向左移动,L 键向右移动。 168 | 这可能现在看起来有点不直观,但是很快就会成为肌肉记忆。 169 | 这是在普通模式下移动光标的基本方法。 170 | 现在,还能做什么呢?如果我们像这样移动文件,速度会非常慢。 171 | 我们不想按住这些键,然后等待 Vim 完成它的操作。 172 | 所以,还有其他不同的按键组合可以进行不同的移动。 173 | 顺便说一下,这些都在讲义中,所以你不需要现在就记住每个键及其含义。 174 | 只要试着理解 Vim 的界面是一种编程语言的总体思想。 175 | 还有一件事情可以做,就是按下 W 键。 176 | 这将使光标向前移动一个单词。 177 | 类似地,B 键向后移动一个单词。 178 | 因此,在同一行内进行更有效的移动。 179 | 还有 E 键可以将光标移动到单词末尾。 180 | 我将它移动到一边。 181 | 182 | 例如,如果我在这里,按“E”键,它会跳到这个单词的末尾,下一个单词的末尾,依次类推。 183 | 你也可以整行移动,所以“0”移动到行的开头,“$”移动到行的末尾,脱字符键(^)移动到行的第一个非空字符。 184 | 让我举个例子。 185 | 在这里,我的光标就在这里;如果我按“0”,我的光标就会移到行的开头,“$”是当前行的末尾;如果我按脱字符键,光标会停留在哪个字符上呢?有人能猜到吗?脱字符键会移到行的第一个非空字符上,有点像正则表达式脱字符。 186 | 没错!它会移到这个连字符上。 187 | 我们再谈谈一些移动命令。 188 | 有一些方法可以在缓冲区中上下滚动,所以Ctrl+U向上滚动,Ctrl+D向下滚动。 189 | 这比按住K或J键要好得多。 190 | 这比整页移动要慢得多。 191 | Ctrl+D和Ctrl+U。 192 | 还有一些方法可以整个缓冲区移动。 193 | 所以,大写的“G”会移动到底部...“gg”会移动到顶部。 194 | 其中一些移动键是助记符;因此,它们更容易记住,例如,“W”是单词,“B”是单词开头,“E”是单词结尾。 195 | 它们都很合理。 196 | 0,脱字符和$符号,有点受到正则表达式的启发,因此它们有点合理。 197 | 198 | 还有一些其他的按键,可能没有太多意义,但是键盘上的键只有那么多,你能怎么办呢?例如,按下 "L" 键将光标移动到屏幕上显示的最低行。 199 | "L" 表示最低,"M" 表示中间,"H" 表示最高,我想这很有道理。 200 | 还有很多其他有趣的移动方式,我们显然无法在此处全部介绍,但你可以通过 Vim 教程练习进行学习,这是本课程的第一个练习。 201 | 现在我想再谈谈其他一些移动方式,也许我只谈论其中一种。 202 | 还有一种叫做 "查找" 的移动方式。 203 | 这也很有用。 204 | 假设我在这一行上,我想跳转到第一个等于...的字符,比如我想跳到第一个 "o"。 205 | 我可以按下 "fo",光标就会移动到第一个 "o"。 206 | 我就好像找到了 "o"。 207 | 我可以按下 "fw" 就会跳到第一个 "w",我想是在这里。 208 | 我可以按下 "fc":查找第一个 "C"。 209 | 我也可以做相同的事情,但是向后移动。 210 | 如果我按下大写的 "F",然后再按下 "w",我就可以找到它前面的 "W"。 211 | 大写的 "F" 和 "s":找到前面的 "s"。 212 | 然后,还有一种查找的变种:t 代表到,所以我可以跳到 O,直到找到 o 为止。 213 | 214 | 但不是在它的顶部,而是在它之前一个字符的位置。 215 | 而大写字母 T,比如说 t,向后跳到字符 t,但不是跳到它的顶部,而是跳到它的前一个字符。 216 | 所以,你已经可以看到我谈到的 Vim 是一种编程语言的想法,你可以组合这些命令。 217 | “F”和“T”是“查找”和“到”的意思,你可以查找特定的字符,或者跳转到特定的字符。 218 | 这些是几个 Vim 的移动命令。 219 | 那么,对于这些命令有没有问题?有问题吗?好的,很棒。 220 | 这些是 Vim 的移动命令,它们可以帮助你在正常模式下快速地在文件中导航。 221 | 现在,另一类有用的命令是编辑命令。 222 | 我们已经谈到的一个是“i”命令,用于从“正常”模式切换到“插入”模式,在那里你可以开始编写文本。 223 | 所以,假设我跳到这里并按下“i”键,现在我可以输入任何文本,比如“Hello World”,然后按下“回车”键。 224 | 然后,按下“escape”键回到正常模式,我就对我的缓冲区进行了修改。 225 | 但是,在处理编程语言时,还有很多其他命令可以进行高效的编辑。 226 | 一个有用的命令是我之前无意中使用过的“o”命令。 227 | 假设我的光标在这里,如果我从正常模式按下“o”,它会在光标下方打开一个新行。 228 | 这就是“o”的含义。 229 | 230 | 它会创建一个新行并进入插入模式,这样我就可以开始输入一些文本,按下Esc键回到正常模式。 231 | 就像“o”命令一样,还有一个大写的“O”命令,所以如果我在这里,按下大写的“O”,它会将我放到当前位置的上方的插入模式中。 232 | 还有另一个Vim命令可以删除东西。 233 | 假设我的光标在这个单词上方,我按下D键。 234 | “D”代表删除。 235 | 噢,没有反应,原来D键需要与移动命令结合使用。 236 | 所以,记得我们刚刚讨论过不同的移动命令,比如hjkl、word、backward word等等。 237 | 所以我按下D。 238 | 哎呀。 239 | 我按下D键,然后按下W键,它就删除了一个单词。 240 | 让我撤销一下。 241 | 在Vim中撤销只需要按u键。 242 | 注意我的光标在这里。 243 | 我按下“dw”:它删除了一个单词。 244 | 我可以移动光标,然后再删除另一个单词。 245 | 246 | 假设我在单词的中间,想要删除到单词末尾。 247 | 你猜我需要使用哪个键的组合呢?是 "d" 和什么?是 "de",没错。 248 | 删除到单词末尾。 249 | 另一个有用的编辑命令是 "c" 命令,c 代表 "change"(改变)。 250 | 所以,改变和删除非常相似,但改变会将你置于插入模式,因为我想删除一个东西,但要将它改变为其他东西。 251 | 因此,如果我在这里,输入 "ce",就像是将其改变到单词的末尾。 252 | 它会删除内容直到单词的末尾,并注意到它将我置于插入模式。 253 | 现在,我输入的任何字符都会进入缓冲区。 254 | 如果我按下 "escape",我就会回到正常模式。 255 | 因此,c 和 d 是相似的:它们都需要一个移动作为参数。 256 | 它们将删除或更改该运动。 257 | 例如,如果你按下 "c" 键,也有这种模式,如果你连续按两次特定的编辑键,它会在给定的行上产生相应的效果。 258 | 所以,如果我按下 "dd",那就会删除该行。 259 | 如果我按下 "cc",那就会删除给定的行,但将我置于插入模式,这样我就可以用其他行替换它。 260 | 我们将涵盖一些其他的编辑命令,因为稍后我们将看到所有这些命令如何相互作用。 261 | 因此,另一个有用的命令是 "x" 命令。 262 | 263 | 假设我的光标在某个特定的字符上。 264 | 如果我按下"x",它只会删除该字符。 265 | 还有另一种命令叫做"r"。 266 | 如果我在某个特定的字符上,按下"r",它会将另一个字符作为参数,并用另一个字符替换该特定字符。 267 | 我将介绍另外几个编辑命令。 268 | 当然,你可以撤销在Vim中所做的更改。 269 | 而且你在普通模式下按下"u"就可以了。 270 | 所以,"u"代表撤销很容易记住。 271 | 我按下了"u"多次;它撤销了我所做的所有更改。 272 | 然后,撤销的相反是重做。 273 | 在Vim中,重做的绑定键是ctrl + R。 274 | 好的,我将讲一下复制和粘贴,因为...哦,是的,有问题吗?那是一个很好的问题!问题是,“'undo'是撤销自进入插入模式以来所做的所有更改,还是只撤销最后一个字符?”实际上比这还要复杂一些。 275 | "撤销"会撤销你所做的最后一次更改。 276 | 所以,如果你进入插入模式,输入了一些东西,然后回到正常模式,然后按下"u"进行"undo",它会撤销你在插入模式中所做的所有更改。 277 | 但是,如果你做了其他类型的编辑命令,比如说我按下"x"删除一个字符...如果我按"u"进行撤销,它只会撤销那个编辑命令所做的更改。 278 | 现在,这回答了你的问题吗?(是的)很好,还有其他问题吗?好的。 279 | 我也会讲一下复制和粘贴,因为那是一个受欢迎的命令。 280 | 281 | y命令表示复制,而p命令则表示粘贴。 282 | y代表"copy",因为yank是Vim用于复制的术语。 283 | 这些命令也可以接受一个动作作为参数。 284 | 例如,如果我键入yy,它将复制当前行。 285 | 如果我按下“p”进行“粘贴”,请注意现在这两行是相同的,因为我刚刚粘贴了一行。 286 | "u"表示“撤销”。 287 | 但是如果我做类似“yw”的操作,它会复制单词。 288 | 然后我可以按下“p”,它会在光标所在位置再次粘贴该单词。 289 | 在复制和粘贴的上下文中,特别有用的一件事是能够选择一块文本并将其复制。 290 | 这可能是您在以前使用的任何编辑器中使用复制和粘贴的方式。 291 | 因此,这就涉及到了视觉模式。 292 | 这些是一组彼此相关的模式,可以从普通模式进入,用于选择文本块。 293 | 其中一个模式是普通视觉模式。 294 | 您可以通过按v进入该模式。 295 | 进入此模式后,您可以使用大多数常规的普通模式命令来移动光标。 296 | 它会选择两点之间的所有文本。 297 | 因此,我可以使用hjkl来移动光标,或使用“w”按单词移动,或使用不同的命令来选择文本块。 298 | 一旦我选择了这个文本块,就有许多不同类型的有用的操作可以进行。 299 | 最常见的操作之一是将其复制。 300 | 301 | 所以,一旦我选择了文本块,我可以按y复制,然后回到正常模式。 302 | 现在,它已经将所选文本块复制到粘贴缓冲区中。 303 | 然后,如果我去其他地方,按下“p”,它会将我刚刚复制的整个文本块粘贴在那里。 304 | 这类似于视觉模式,可以选择连续的文本流。 305 | 有视觉行模式:通过按大写V可到达该模式,并且一次选择整行。 306 | 然后还有视觉块模式,可以通过按“control” +“V”来选择矩形块文本。 307 | 因此,这是您以前的编辑器无法完成的操作。 308 | 好的,还有很多其他的 Vim 编辑命令需要学习。 309 | 有许多非常奇怪和花哨的事情。 310 | 例如,波浪线命令可以更改字符或当前所选内容的大小写。 311 | 因此,例如,我可以选择 Visual Studio Code 并翻转整个内容的大小写,然后按波浪线键。 312 | 还有许多类似的东西。 313 | 它们随着你的学习会变得越来越深奥。 314 | 因此,我们不会涵盖所有这些内容,但您将在练习中了解到它们。 315 | 这些是 Vim 编辑命令,其中许多可以与移动命令组合使用。 316 | 对于以上内容是否有疑问呢?很棒。 317 | 接下来,与正常模式相关的另一个命令类别是数量。 318 | 您可以给它们一个数字,以执行某个特定操作一定数量的次数。 319 | 320 | 假设我的光标在这里,我想要向下移动1 2 3 4行。 321 | 一种方法是按下“j”键四次,向下移动四次,而“kkkk”则向上移动四次。 322 | 但是,我可以使用计数来代替多次按同一个键。 323 | 所以,如果我按下“4”、“j”,它会执行四次“j”操作,对吧?Vim的界面就像是一个编程语言。 324 | 如果我按下“4k”,它会向上移动四次。 325 | 如果我在这里,按下“v”进入可视模式...好的,现在我可以移动光标并选择文本块。 326 | 我可以按“eee”选择几个单词,但我也可以回到这里 - 按“v”进入可视模式 - 并按三个“e”选择向前的三个“单词结尾”。 327 | 当然,这些还可以与编辑命令相结合使用。 328 | 比如,假设我想删除七个单词。 329 | 我可以把光标移动到某个位置,然后输入“7dw”。 330 | 删除七个单词。 331 | 这对于这样的情况特别有用:我的光标在屏幕上的某个位置,我正在查看屏幕上的其他地方,或者我想让光标移动到那一行。 332 | 请注意,我在左边设置了相对行号。 333 | 因此,无论我的光标在哪里,它都显示当前行号,但在其他地方,它只是相对于我的位置的偏移量。 334 | 335 | 现在假设我的光标在这里,但我想向下移动到像“Microsoft Word”这样的位置,也就是往下8行。 336 | 那么,我应该按哪些键的组合来实现呢?也就是说,最有效的方法是什么?是的,就是这样!我们试一试——按下8j,我的光标就移动到了这一行。 337 | 好的。 338 | 最后,Vim中的一个关键意义类别是修改器。 339 | 到目前为止,我们已经介绍了移动、编辑、计数,最后是修改器。 340 | 修改器可以稍微改变移动命令的含义。 341 | 其中两个特别有用的修改器是“a”和“i”。 342 | a代表around(周围),i代表inside(内部)。 343 | 为了看到这个特别有用,我可以把光标移动到这里,例如。 344 | 希望你们中大多数人都熟悉markdown语法——如果不熟悉也没有关系。 345 | 这是一个markdown链接;方括号中呈现出一段文本,括号中则是链接。 346 | 假设我的光标在这里,我想更改与此链接相对应的文本。 347 | 嗯,我可以通过向后移动b,然后2dw,再按“i”进入插入模式来进行更改。 348 | 这是我可以进行此更改的众多方法之一,我可以输入任何其他想要的内容——按u撤消,再按一次u。 349 | 我还可以使用另一种方法,即更改两个单词——“c2w”,然后输入其他文本。 350 | 351 | 但是,我可以用修饰符命令来实现相同的更改,以说明我想如何与括号和方括号这些不同类型的分组交互。 352 | 因此,最后一种方法是更改方括号内部-“c”“i”“[”-这将使我进入插入模式,删除方括号内部的内容后。 353 | 你看到我们如何将所有这些不同的元素结合起来,例如我们谈到的“更改”,我们可以将其与不同的移动命令组合在一起。 354 | 我们谈到了“inside”修饰符。 355 | 然后我们谈到了...我们没有谈到括号。 356 | 但是,如果你的光标悬停在不同的分组元素,如括号或方括号上,你可以按百分号移动键在匹配的括号之间来回跳转。 357 | 如果我走到这里并输入“d,i,(”,我可以删除括号内的内容。 358 | 因此,这些是Vim的修饰符。 359 | 我想我们谈到了“i”,但是我们没有谈到“a”。 360 | 如果我执行“da(”,它将删除一个完整的、包括括号的分组,所以“i”表示内部,而“a”表示周围或包括。 361 | 这些基本上是您在与Vim界面交互时可以组合在一起的不同类别的东西。 362 | 所以,有关这一点或这个界面作为编程语言的整体思路有任何问题吗?很酷。 363 | 那么,让我们做一个快速演示,以展示此编辑器的强大功能。 364 | 它将帮助我们了解这个工具如何快速运作,并与我们思考的速度相匹配。 365 | 这里有一个非常破损的“fizzbuzz”实现,实际上什么都没有打印。 366 | 希望你们中的大多数人都听说过“fizzbuzz”,如果没有,我将简要解释一下。 367 | Fizzbuzz是一项编程练习,在此练习中,您将打印从1到n的数字,但当数字可以被3整除时,您将打印fizz-当它可以被5整除时,您将打印buzz。 368 | 369 | 你要打印FizzBuzz,如果不符合条件,就直接打印数字。 370 | 所以你应该打印1,2,Fizz,4,Buzz等等。 371 | 但是,如果我运行这个程序,它不会打印任何东西。 372 | 在左边,我把它们放在终端里。 373 | 右边,好的,所以这里有一堆问题。 374 | 其中一个问题是main从来没有被调用,所以让我们从修复它开始。 375 | 这就是我会如何进行更改的方式,请注意它只需要很少的按键:大写G表示跳到文件底部,o表示在下面打开一个新行,现在我可以输入一些东西了。 376 | 所以,我处于插入模式。 377 | 好的,我输入了我想要做出的任何更改,按escape回到正常模式。 378 | 如果我输入:w(命令模式,右边),让我回到这里。 379 | 好的,现在至少当我运行它时,我的程序会打印一些东西。 380 | 这个程序的另一个问题是它从0开始而不是从1开始,所以让我们去修复它。 381 | 所以,我想去到这个范围(哎呀,这个范围的问题),它不应该从0到限制,而应该从1到限制加1。 382 | 我没有向你展示过的一个命令是如何在vim中搜索,所以按斜杠(/)关闭它并重新开始。 383 | 如果你按斜杠,它会开始搜索,所以如果我输入“范围”(enter),我的光标会从之前的任何位置跳转到它找到的第一个“范围”。 384 | 385 | 所以,这是一种非常高效的移动方式。 386 | WW可以向前移动两个单词,I可以进入插入模式,添加“1,”(逗号空格),然后退出,回到正常模式。 387 | 这是Vim中非常常见的模式:保持在正常模式下,移动到某个地方,进入插入模式,进行微小的更改,然后立即返回正常模式,就像正常模式是主页一样,大部分时间都应该在那里。 388 | 我还想添加一个“+1”,所以按E键到这个单词的末尾,按a进入插入模式,然后输入“+1,”退出。 389 | 好了,问题解决了。 390 | 这个程序的另一个问题是对于能被3和5整除的数都打印“fizz”,所以让我们来解决这个问题。 391 | 输入斜杠fizz搜索"fizz",按E键进入引号内部,更改引号内的内容,删除“fizz”,然后按Esc回到正常模式。 392 | 很好,我解决了这个问题。 393 | 这个程序的另一个问题是,对于15的倍数,它在不同的行上打印“fizz”和“buzz”,所以我们要去解决这个问题。 394 | 让我到达这一行。 395 | 在Vim中,我可以非常有效地更改这个程序,而不必真正关注程序的内容。 396 | 我的光标在这一行上。 397 | 按美元符号将光标移动到行尾,按i进入插入模式,然后输入一些内容,按Esc回到正常模式。 398 | 现在,我想进行与下面的打印相同的更改。 399 | 400 | “JJ dot”这个命令在vim中是重复执行前一个编辑命令,这是一种不用一遍又一遍地输入相同内容来完成重复任务的好方法。 401 | 在这个例子中,它插入了“逗号、结束引用号”,并且当我按下dot时,它在这一行上执行了同样的操作。 402 | 接下来,我们将解决这个程序的另一个问题,即它可能应该使用命令行参数来代替这里硬编码的10。 403 | 那么我们该怎么做呢?我按GG回到开头,然后按大写O,在上面打一行文字,如“imports this”,回车,按escape回到正常模式,然后我想到达这里的10,所以输入/10就跳到那里了。 404 | CI pren用于编辑括号内的内容,现在我可以在这里输入任何我需要输入的内容,一旦完成,我的程序就能正确地执行fizzbuzz了。 405 | 我想我漏了一个我想要的修改,但这没关系。 406 | 这证明了您可以快速地进行许多更改。 407 | 对于这个演示或我们一直在谈论的总体思路,有任何问题吗?好的,这将在周二进行介绍,我在左边运行vim的外部环境和右边的shell,这是外部的team ox。 408 | 一个相关的问题可能是如何在不同的vim窗口之间切换,您可以查看讲座笔记中的快捷键,可以在同一个窗口或多个窗口之间进行切换。 409 | 问题:啊,好问题。 410 | 所以,delete命令接受一个运动命令,然后删除这些内容但保持在正常模式下,所以您可以继续执行其他操作。 411 | 而change命令非常类似于delete命令,它也接受运动命令并以相同的方式处理它们,在文件中移动并删除这些内容。 412 | 413 | 但是它随后会进入插入模式,从而节省了您打一个额外按键的时间。 414 | 例如,如果我在这里,我想删除“main”,DW删除一个单词,但现在如果我按下J这样的按键,它会让我向下移动。 415 | 如果我撤消这个操作,再输入cw4更改一个单词,现在它实际上已经进入了插入模式,我可以输入任何我想要插入的内容。 416 | 因此,DWI与CW相同,但它节省了一个按键。 417 | 我们在资源中提供了一个名为“vim golf”的东西。 418 | 基本上,人们在网上设置了一个游戏,您可以得到一个编辑任务,并尝试找出完成编辑所需的最小按键数。 419 | 它实际上非常容易上瘾。 420 | 所以我只建议在空闲时间进行游戏。 421 | 我想我看到有人举手提问了。 422 | 对,句号。 423 | 这是vim命令中最有用的之一。 424 | 好问题。 425 | 还有其他问题吗?太棒了。 426 | 所以我想我们还有大约五分钟的时间,我将简要介绍一个在笔记中也详细介绍的东西,请确保查看这个笔记。 427 | Vim是一款程序员的文本编辑器,因此,它具有高度的可编程性。 428 | 不仅通过它的界面(它本身就是一种编程语言)而且还有其他几种方式。 429 | 有许多设置可以进行微调以匹配您的偏好,您还可以安装插件来完成各种有用的事情。 430 | 431 | Vim 的配置方式是通过一个名为 vim RC 的磁盘文件来实现的,这在很多基于 shell 的工具中都是一种常见模式。 432 | 这个纯文本文件配置了工具的运行方式。 433 | 如果我编辑这个文件,它可能还没有在你的机器上存在,但我们已经为你创建了一个默认的 vim RC 并在课程网站上提供了链接,你可以从那里开始。 434 | 如果我输入 vim tilde / boom RC,我可以看到一堆注释,然后是特定的命令,比如默认情况下,我们希望有语法高亮和行号。 435 | 如果我们没有进行一些配置,比如让我删除设置行号的那些内容,如果我删除了这些配置并重新启动 vim,注意到左边就没有行号了。 436 | 总之,你可以配置很多东西,我们已经为你提供了一个非常基本的配置,试图消除 Vim 中默认情况下的一些奇怪行为,但我们不会在你身上强加太多的其他观点。 437 | 当然,像我们三个人一样,都经常使用 Vim,我们已经深度定制了 VimRC。 438 | 所以如果你想借鉴一些东西,我们也链接了我们的个人配置,此外,像数千万人一样,他们会在 Github 上分享他们的 VimRC,所以有很多地方可以寻找灵感。 439 | 在 Vim 中,你还可以使用插件来扩展它,这些插件可以做很多有用的事情。 440 | 这让你可以做一些模糊文件查找之类的事情,这是很多文本编辑器默认就有的,所以你可以得到一个弹出窗口,输入一个文件名或大致的文件名,非常快速地找到它,或者有些插件可以显示撤销历史的可视化效果,有些插件可以显示文件浏览器等等。 441 | 所以我们在课程网站上链接了一些我们最喜欢的插件,我强烈建议你熟悉如何安装插件,因为这只需要三秒钟,而且其中一些非常酷。 442 | 443 | 最后一个主题是 Vim 模式和其他程序,我会简要提及。 444 | 原来许多程序员都对 Vim 的界面感到兴奋,因此他们在其他工具中实现了类似的功能。 445 | 例如,我已经将 Python REPL 配置为在 Vim 模式下运行。 446 | 因此,我可以在这里键入内容,如果我按下 escape 键,现在我就在 Python REPL 中的普通模式中了。 447 | 我可以前后移动,按 X 删除内容,使用 CW 更改一个单词等等。 448 | 不仅如此,我的终端也是这样工作的。 449 | 我可以在这里键入任何内容,然后按 escape 键,就在普通模式中了。 450 | 我可以进入终端内部的可视化模式,选择文本块,按 tilde 键更改大小写,等等。 451 | 因此,我们提供了如何为 bash、zsh、fish、基于 readline 的程序(如 Jupyter Notebook)启用 Vim 模式的链接,还有许多其他工具。 452 | 如果没有提供链接,您可能可以通过搜索找到它,因为许多人喜欢这种功能。 453 | 如果您真的打算学习 Vim,我认为在使用的每个工具中启用这种 Vim 模拟模式是很有价值的。 454 | 一是可以更好地学习工具,二是一旦你变得擅长 Vim,这些技能将转移到你使用的所有其他工具上。 455 | 好的,这就是我们对 Vim 的快速介绍。 456 | 我们无法在今天的讲座中涵盖所有有趣的内容,但这些内容在讲义中都有涉及。 457 | 最后,我强烈建议您完成今天的练习。 458 | 至少对我个人来说,我认为学习文本编辑器是我们这门课教授的最有益的事情。 459 | 好了,今天的讲座就到这里,我们明天见。 460 | 请注意,我们已将明天的讲座改为数据整理。 461 | 周二和周四的讲座现在已经互换了。 462 | 如果有人只打算来听其中一个讲座,请查看课程网站。 -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture09_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 好的,那么,让我们开始今天的讲座吧。 2 | 今天,我们将讨论安全和密码学,今天的讲座会与我们去年课堂上对这个话题的处理有些不同。 3 | 去年,我们更注重从计算机用户的角度讨论安全和隐私问题,但今天我们将更多地关注安全和密码学概念,这些概念对于理解我们在这门课上早些时候讨论的一些工具非常重要。 4 | 例如,我们在Git讲座中讨论了哈希函数或加密哈希函数(如sha-1),在命令行环境讲座中讨论了公钥。 5 | 因此,今天我们将更详细地讨论这些不同的密码原语,了解它们的工作原理以及它们在这些不同的工具中的使用方式。 6 | 这个讲座不是替代更严格的安全课程的,麻省理工学院有很多非常好的课程,例如6.858,它是关于计算机系统安全的,或者6.857和6.875,它们更加专注于密码学。 7 | 因此,在没有接受这些课程或其他地方的安全形式培训的情况下,请不要从事安全工作。 8 | 除非你是专家,否则不要自己开发加密算法,不要构建自己的加密实现或协议。 9 | 同样的原则也适用于计算机系统安全。 10 | 这个讲座不是关于构建自己的东西,而是关于了解已经存在的东西。 11 | 因此,我们认为这个讲座将非常非正式但实用地处理这些基本的密码学概念,并希望它能帮助你理解我们在这门课上早些时候讨论的一些工具。 12 | 今天的讲座计划有什么问题吗?太好了。 13 | 那么今天的第一个话题是熵。 14 | 熵是随机性的一种度量,这在尝试确定密码强度时非常有用。 15 | 让我们看看xkcd的这个漫画,我们很喜欢xkcd漫画。 16 | 这个漫画举手如果你以前看过这个漫画。 17 | 【暂停】好的,你们中有很多人。 18 | 这个漫画抱怨了这种常见的模式,这种模式被教给计算机用户,即当你设计密码时,它们应该是像“t#0rU&b40rM $”或“p3@ch3$”这样的字符串,就像我们应该设计充满有趣字符和其他内容的密码,以使攻击者很难猜测。 19 | 然而,事实证明,像这样的密码实际上相当脆弱,易于被能够快速猜测密码和暴力攻击的计算机所猜测。 20 | 另一方面,像左下角的"correct horse battery staple"这样直觉上看起来不太安全的密码,却被证明更安全。 21 | 那么我该如何量化这些不同密码的安全性呢?通过测量密码中的随机性数量,即其中有多少比特的随机性。 22 | 因此,熵是以比特为单位进行度量的。 23 | 24 | 这段话主要是在讲如何量化密码的安全性,通过计算密码中包含多少随机性的“熵”来衡量。 25 | 熵是用比特(bits)来度量的,和信息论中的熵类似。 26 | 在简单的情况下,我们只需要考虑从一组事物中随机选择的情况。 27 | 例如,构造一个由四个随机单词组成的密码时,我们可以从一个包含一百万个单词的字典中随机选择每个单词。 28 | 通过计算可能性的数量,就可以得出熵的度量,即可能性数量的以2为底的对数。 29 | 在这个例子中,如果有一千个可能性,那么攻击者每秒尝试猜测一千个密码,这不是一个很好的密码。 30 | 熵的度量也决定了攻击者需要多长时间才能破解密码。 31 | 对于硬币抛掷,因为只有两种可能性,所以熵是1比特。 32 | 对于骰子掷出的6个面,有6种可能性,所以熵是2.6比特。 33 | 在设计密码时,我们需要考虑生成密码的模型,例如,在左上角的例子中,我们可以将一个词典单词的某些字符替换为类似的数字,然后在末尾加上一个标点符号和一个数字,我们可以使用常规语言来计算这样的密码有多少种可能性,然后计算出熵。 34 | 在这个例子中,密码的熵为28比特。 35 | 而在左下角的例子中,“correct horse battery staple”,我们假设有一个包含2000个单词的词典,将其中四个单词组合起来,就可以获得大约44比特的熵,比前面的例子更加安全。 36 | 在设计密码时,我们需要考虑我们要保护的内容和受到攻击的威胁模型,不同的威胁模型需要不同的熵来保护密码的安全。 37 | 38 | 例如,这个漫画提到了一个攻击者可以每秒猜测一千个密码。 39 | 这可能对某些允许人们尝试使用您的电子邮件和随机密码进行登录的网络服务来说是可能的。 40 | 但是,这个每秒猜测一千个密码的模型可能对其他情况不准确。 41 | 例如,离线密码破解场景或者攻击者已经破解了一个网站并下载了它们的数据库,他们有一种混淆形式的您的密码,并且正在尝试找出密码是什么。 42 | 也许他们可以并行化这种攻击,使其每秒进行一百万次猜测。 43 | 猜测次数取决于您需要保护自己免受什么样的攻击。 44 | 但是,对于需要受到保护的网站并且您担心在线密码猜测的情况,大约四十个比特的熵可能足够了。 45 | 如果您担心离线攻击并且想要真正、真正地安全,那么可能需要八十比特的熵。 46 | 这些是您可以使用的大致指南。 47 | 那么,如何生成强密码呢?首先,您需要一些密码模型。 48 | 例如,常用的字典词组,您可以得到一个字典。 49 | 然后,您可以使用类似于掷骰子的方法。 50 | 有一首歌曲在讲义中有链接,您可以获得物理骰子并将其滚动,然后将掷骰结果映射到字典词语,以最终将其转换为密码。 51 | 这样做的好处在于,使用一些您知道是随机的物理标记(如平衡骰子或硬币)是一个好习惯,因为人类实际上并不擅长选择随机数字。 52 | 如果我让您从1到100中随机选择一个数字,您很有可能不能很好地以均匀随机的方式选择。 53 | 这就是为什么使用这些物理标记来产生随机性是好的。 54 | 熵是我们的第一个概念。 55 | 有关此事或此漫画的任何问题吗?好的。 56 | 那么进入稍微有趣和复杂的主题,下一个我们要谈论的是哈希函数。 57 | 希望你们中的大多数人都参加了上一次关于Git中使用的SHA-1哈希函数的讲座。 58 | 现在更详细地讨论这个主题,哈希函数在高层次上是将可变数量的数据映射为固定大小输出的函数。 59 | 这是这个特定哈希函数的类型签名。 60 | 这些函数具有一些有用的属性。 61 | 从高层次来看,它们可以被视为难以反演的函数,其输出看起来是随机的。 62 | 我们可以在一些随机数据上实际尝试这个函数。 63 | 例如,如果我在我的终端中输入 "printf hello",它将按照你所期望的那样打印出字符并将其导出到标准输出,然后我可以将其导入 SHA-1 校验和命令。 64 | 65 | 这是一个命令行程序,通过标准输入接受输入,并计算SHA-1函数,该函数从输入中获取一些可变数量的字节并生成一个160位的输出,该输出在这种情况下表示为十六进制字符串。 66 | 因此,它是一个长度为40的十六进制字符串,并且您可以在此处看到此输出。 67 | "-"只是表示它从标准输入中获取了输入。 68 | 因此,此输出看起来像某些随机数字,但重要的一点是,这是一个确定性数字。 69 | 如果您在自己的计算机上尝试相同的命令“printf hello | SHA-1 something”,您将得到相同的输出。 70 | 因此,SHA-1是一些人们已经就其所有参数达成一致的著名函数。 71 | 我们将看到,如果我们略微调整输入,比如说将“hello”更改为一个大写的“Holo”,现在我得到了一个完全不同的输出。 72 | 即使它是确定性的,它也看起来像某种其他随机的数字,并且您可以在自己的计算机上再现它。 73 | 哈希函数具有许多重要属性。 74 | 密码哈希函数具有的第一个属性是它们是不可逆的。 75 | 这意味着,如果您获取此函数的输出,例如,即“aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d”,则很难弄清楚产生该输出的输入是什么。 76 | 因此,您可以很容易地计算SHA-1哈希值,但是您无法逆向操作。 77 | 这些函数具有的另一个属性是它们是抗碰撞的。 78 | 这个属性的意思是很难找到两个不同的输入产生相同的输出。 79 | 因此,这基本上描述了什么是密码哈希函数。 80 | 那么,这些哈希函数实际上有什么用途呢?嗯,我们已经在Git中看到了内容地址存储的一个应用。 81 | 因此,我们希望得到一种统一的方式来命名存储在对象存储中的不同对象,而Git将它们全部命名为它们的SHA-1哈希。 82 | 因此,您拥有要存储的实际数据,然后要为该特定数据命名,只需命名SHA-1哈希即可。 83 | 所有这些都以特定方式存储在对象存储中。 84 | 我们在查看Git的许多不同部分时看到了这一点。 85 | 例如,右边这里,我进入Git存储库。 86 | 如果我执行'git log',它会显示提交。 87 | 例如,这个数字上面是密码哈希函数SHA-1应用于描述此特定提交的提交对象。 88 | 那么,有人知道为什么Git在这里使用密码哈希函数而不是......你可能在其他计算机科学课程中听说过称为散列函数的东西,没有前面加上“密码学”这个词。 89 | 90 | 它们具有类似的属性,可以将可变大小的输入转换为某个固定大小的输出。 91 | 但是它们并没有完全具备难以找到产生特定输出的输入等属性。 92 | 这是一种比较弱的定义。 93 | 那么,为什么在 Git 中我们关心拥有一个密码哈希函数而不仅仅是一个普通的哈希函数?大家有什么想法吗?是的,基本上就是这样,我们不希望这个哈希函数的输出产生冲突。 94 | 就像每个提交都由一个哈希函数标识,每个文件都由该文件的哈希标识。 95 | 如果在实践中有两个不同的内容产生了相同的输出,也就是说,如果该函数不具有抗碰撞性,那么这可能会造成很大问题。 96 | 因为你和我,我们可能会为了得到我们认为是相同的 repo,检出相同的提交哈希,并且我们可能会得到不同的文件。 97 | 这是令人担忧的,因为 Git 用于跟踪软件、跟踪软件的开发,同时也涉及确保正确的人员在编写软件,没有发生任何有趣的事情。 98 | 例如,有许多开源项目,如 Linux 内核,使用 Git 进行开发。 99 | 如果某个 Git 贡献者能够编辑某个文件并提出一些看起来相当良性的更改请求,比如“让我改进一下 Linux 的这个部分”,提交该更改请求给 Linux 开发人员,然后在实践中实际提供一个具有相同提交哈希等的 Git 存储库,但实际上文件内容是不同的,那么就有一些恶意行为。 100 | 因此,Git 实际上依赖于这个 SHA-1 函数作为密码哈希函数,以实现安全性。 101 | 还有其他一些有趣的哈希函数应用吗?所以,就像我们看到的那样,哈希函数将大输入转换为小输出,而且由于哈希函数是抗碰撞的,输出可以用来验证或标识输入。 102 | 因此,您可以将哈希视为文件的简短摘要。 103 | 例如,在此目录中的一堆文件中,我可以计算此 readme MD 文件的 SHA-1 校验和。 104 | 有趣的是,计算出另一个具有相同哈希输出的文件在计算上是计算上困难的,或者你可以认为是不可能的。 105 | 在从互联网下载文件的情况下,这种场景非常有用。 106 | 例如,有许多 Linux 发行版从其网站分发大型 CD 或 DVD 映像。 107 | 像我可以到 Debian org 上下载最新版本的 Debian。 108 | 问题是,托管这些文件可能是昂贵的。 109 | 因此,许多人很好心地提供这些文件的镜像。 110 | 所以,我可以去许多其他站点之一,而不是从Debian org下载Debian,下载那些在Debian org托管的应该是相同的文件。 111 | 但是,我怎么知道我实际上得到了正确的文件?比如,如果我设置了一个恶意镜像,你去了类似Anisha是恶意Debian网站的地方然后尝试下载Debian,结果你的Linux安装被植入了后门。 112 | 好吧,你可以做的一件事就是从原始的Debian网站下载一份副本,然后下载我的版本并进行比较。 113 | 114 | 事实上,托管这些文件可能会很昂贵。 115 | 因此,很多人都很友好地托管这些文件的镜像。 116 | 因此,我可以不从Debian.org下载Debian,而是可以去许多其他站点之一,并下载应该是Debian.org托管的相同文件。 117 | 但是,我怎么知道我是否真的得到了正确的文件呢?比如,如果我设置了一个恶意镜像,你去像Anisha is evil Debian website com这样的站点尝试下载Debian,结果你的Linux安装被后门入侵了。 118 | 那么,你可以做的一件事是从原始的Debian网站下载一份副本,然后再下载我的版本并进行比较。 119 | 但这有点违背了初衷,对吧?因为我们希望避免从Debian.org下载文件,因为托管这些文件很昂贵,我们希望让许多不同的人能够在其他地方镜像文件的副本。 120 | 那么,有没有人看到加密哈希函数如何有用来解决这个问题?我想从一个不受信任的来源下载一个文件,而不是从受信任的来源本身下载文件。 121 | 但也许我可以从这个受信任的来源获取一些小的信息,以便知道我从不受信任的来源下载的文件是否是我应该得到的东西。 122 | 是的,它基本上只是一个加密哈希函数的简单应用。 123 | 因此,Debian.org可以生成它们的正确ISO文件或其他文件,并且不是在其网站上发布文件本身,而是发布该文件的哈希值。 124 | 与文件本身相比,这个哈希值可能只有160位数据,在这种特殊情况下非常便宜。 125 | 然后,作为用户,我可以从任何随机网站下载该文件,可能是一个不受信任的网站,下载完成后,我只需双重检查sha-1哈希值。 126 | 如果哈希匹配,则我知道我有正确的文件,因为对于某人来说,给我一些不同的文件,而这些文件恰好具有相同的哈希值是计算上不可能的,因为哈希函数是抗碰撞的。 127 | 对此应用有任何问题吗?是的,这是一个很好的问题,为什么需要不同的人来托管信息?难道对于每个人来说都同样昂贵吗?这个问题的答案有点复杂,但这是部分答案。 128 | 一件事是从服务器下载文件受到服务器距离你有多远的影响。 129 | 举个例子,如果服务器在马萨诸塞州而您在中国,您必须通过互联网进行大量的往返,这可能会因为许多原因而变得昂贵,比如延迟高,流量需要通过许多不同的电线才能到达您所在的位置。 130 | 因此,这些网站所做的一件事情就是将他们的内容分发到世界各地的服务器上,然后作为用户,您从最接近您的服务器下载。 131 | 比如,MIT维护一个Debian软件包存储库,镜像了所有的Debian软件。 132 | 因此,如果您是MIT的Debian用户,您可以使用MIT的所有内容副本,然后通过我们快速的本地网络访问,这样流量根本不需要通过外部互联网,所以非常快速。 133 | 这是个好问题。 134 | 还有其他问题吗?好的,最后一个有趣的哈希函数应用是一种称为承诺方案的东西。 135 | 我想玩一个游戏,我需要一个志愿者。 136 | 所以你实际上不需要从座位上站起来什么的,我只需要你跟我交流一下。 137 | 有人愿意举手自愿吗?好的,你叫什么名字?Abdul Aziz?好的,太好了。 138 | 所以,Abdul Aziz,我们要玩一个游戏,我要掷一枚硬币,然后你要叫正面或反面,如果你猜对了,你就赢了,如果你猜错了,你就输了。 139 | 这个游戏没有任何利益关系,只是赢得荣誉。 140 | 不幸的是,我检查了一下我的钱包,只有美元,没有硬币。 141 | 所以,我只能在脑海中掷硬币。 142 | 好的,我掷硬币了,请你叫正面或反面。 143 | 144 | 很抱歉,你输了,是正面。 145 | 我可以作弊,对吧?我可以看到你说的并说相反的话。 146 | 所以让我们试着修正一下这个游戏。 147 | 如果我说翻转结果是正面或反面,你再猜,怎么样?好的,如果我说“结果是反面”,你会怎么猜?你会选反面?是的。 148 | 那么,我们是否可以在没有共享物理硬币的情况下公平地玩这个“猜硬币翻转结果”的游戏呢?因为我无法真正操纵你的物理现实。 149 | 如果我在你面前翻硬币,你可能会相信它是好的,对吧?所以,事实证明,哈希函数通过一种称为承诺方案的思想为我们提供了一种解决这个问题的很酷的方法。 150 | 因此,我可以说:“这里是解决方案的构造。 151 | 我可以选择正面或反面,实际上我要选择一个大的随机数,比如这个数,在这个时刻,你还没有看到这个数,我只是在脑海中做所有这些。 152 | 然后,我告诉你:“好的,我翻了个硬币,我现在不会告诉你结果,因为你还没有猜正面或反面,但我会告诉你结果的SHA-1摘要。 153 | 这是这个值。 154 | 现在,在此之后,你可以猜正面或反面了。 155 | 所以,你选什么?选正面。 156 | 那么,我可以向你揭示我的输入值,然后你可以进行交叉检查,对输入进行SHA-1摘要以验证输出是否与我之前所说的一样,然后我们可以找到一种将这些数字映射到正面或反面的方法。 157 | 所以,我可能事先同意偶数为正面,奇数为反面,这是一种修正游戏的方法。 158 | 所以,我们实际上可以在我们的脑海中玩这个游戏。 159 | 我可以选择一个值,但不向你透露该值,但我可以承诺该值。 160 | 因此,这是一种绑定承诺方案,我在告诉你之后无法改变主意,但不会向你透露原始值。 161 | 因此,这是加密哈希函数的另一个不错的应用。 162 | 对于这个特定的构造有任何问题吗?好的。 163 | 接下来,我们将讨论密钥派生函数,通常缩写为KDF。 164 | 这是一个与哈希函数非常相似的概念,除了它有一个额外的特性,即计算速度较慢。 165 | 举个例子,有一个被称为PBKDF2的哈希函数或密钥派生函数,它具有与我们在这里谈论的这些哈希函数类似的形式,它们接受某些可变长度的输入并生成固定长度的输出。 166 | 167 | 但它们通常被用于一特定目的。 168 | 这个目的通常是将固定长度的输出作为另一个密码算法中的密钥。 169 | 我们稍后将讨论这些算法,比如使用这个函数的输出的用途。 170 | 但这些东西的一个特性就是它们很慢。 171 | 有没有人知道为什么要让算法变慢?通常我们希望算法很快,对吧?那么为什么我们希望算法很慢呢?是的,没错,就是这个原因。 172 | 我重复一遍,这样话就可以被录音了。 173 | 你希望它变慢的原因是在实际使用它进行密码认证时。 174 | 你保存了密码的哈希值,然后有人输入密码,你想知道它是否与哈希值相对应。 175 | 它慢一些也没关系,因为你只需要进行这个检查一次。 176 | 但另一种使用这个函数的情况是当有人试图暴力破解密码时。 177 | 比如一个网站的密码数据库被盗,有人在尝试破解所有密码。 178 | 那么在这种情况下,你希望这个函数变慢,因为有人将要做这个操作数百万次。 179 | 通过使这个函数变慢,你可以大大减缓攻击者的速度。 180 | 所以,如果这需要一秒钟才能计算出这个函数,这也没关系。 181 | 但当你进行暴力破解时,我们不会像那个 XKCD 漫画中一样每秒尝试一千次。 182 | 我们可以让它慢一点。 183 | 那么密钥派生函数的输出实际上用于什么呢?好的,我们接下来要讨论的,可能是当你想到密码学时最经典的东西之一,就是加密和解密。 184 | 接下来的话题是对称密钥密码学。 185 | 就像这节课的其余部分一样,我们不会讨论如何实现它们,而是会讨论对称密钥密码学的 API,就是它们如何被使用。 186 | 对称密钥密码系统有几个不同的函数。 187 | 它们有一个密钥生成函数,这是一个随机函数,生成我们称之为密钥的东西。 188 | 然后它们有一对函数,加密和解密。 189 | 加密将我们所称的明文作为输入,这只是一些字节序列,一些数据。 190 | 它采用一个密钥,这是一个来自这个密钥生成函数的输出,然后产生我们所称的密文。 191 | 192 | 然后解密函数则是相反的过程。 193 | 它需要密文和密钥,产生明文。 194 | 这个三个函数有几个属性。 195 | 其中之一,就像你预期的一样,这个函数并没有告诉你加密输入的所有信息。 196 | 所以第一个属性是,只有在拥有密钥的情况下,你才能通过密文找出明文。 197 | 另一个属性是一种显而易见的正确性属性,即如果你使用密钥加密某个消息M,然后使用相同的密钥解密该密文,那么你将得到相同的消息。 198 | 这是一种显而易见的正确性属性。 199 | 这个描述听起来合理吗?它是否符合你对于将某些数据模糊化以便你无法知道原始输入的直觉理解,然后通过某个解密函数以该密钥来检索原始输入的理解呢?虽然这不是关于安全性的严格定义,但这是一个足够好的直观定义,我们可以使用它。 200 | 那么,这个描述有没有问题?对于对称密钥密码学的应用场景在哪里呢?我们将在这个讲座后面讲解许多例子,但现在我们讲解一个例子,即将文件加密以便存储在一个不受信任的云服务中。 201 | 考虑像Dropbox或Google Drive这样的服务,你上传文件并信任服务不会查看你的文件或者对其进行任何恶意操作。 202 | 这些服务至少在我列举的这些中并没有进行加密等操作。 203 | 理论上,这些公司的任何员工都可以查看你的文件。 204 | 当然,这些公司有许多政策和技术控制来确保这种情况不会发生,但这并不意味着它在技术上不可能发生。 205 | 如果你不想信任这些云服务,不想让他们窥视你的数据,或者进行其他你不想要的操作(比如进行机器学习),那么你可以在上传这些文件到网络服务之前对其进行加密。 206 | 那么这个想法有道理吗?我可以将我的文件(比如中心照片)通过一个加密函数,产生密文,然后将该密文安全地放置在网络服务上进行备份等操作,如果我需要该文件,我可以检索该密文,然后使用我的密钥将其解密回明文,然后将结果用于我需要做的任何操作。 207 | 这个想法有道理吗?是的,这是一个好问题。 208 | 问题是,任何人都可以通过相同的加密程序进行加密吗?或许我应该更详细地解释一下这个密钥生成函数是随机的,并且这个密钥具有高熵值。 209 | 回到我们之前讨论的主题,比如我们可能会使用AES-256。 210 | 这是一种特定的对称密码,它的名称可能暗示了它的密钥具有256位熵。 211 | 这意味着只要攻击者(即从网络服务下载密文的人)不知道你的密钥,除非他们有更好的攻击手段,否则他们将不得不尝试所有可能的密钥。 212 | 如果有2的256次方个密钥,那么在合理的时间内尝试所有密钥是不可能的。 213 | 这回答了你的问题吗? 214 | 好的,还有其他问题吗?这是一个很好的问题,也引出了我接下来要谈论的话题。 215 | 所以感谢你的提问。 216 | 正如你所指出的,如果我丢了我的密钥,我就有点卡住了,对吧?我需要我的密钥来解密。 217 | 这就是这个东西的重点。 218 | 如果我不需要我的密钥来解密,那么这个加密系统就不会很好。 219 | 所以我可以将这个对称密钥加密的想法与我们刚才谈论的密钥派生函数相结合。 220 | 因此,我不再使用一些通过密钥生成函数随机生成的密钥,例如从计算机中的某个位置获取熵,而是使用一个口令,并将其通过我的密钥派生函数处理后得到我的密钥。 221 | 然后,我可以将我的明文和密钥结合在我的加密函数中,从而产生我的密文。 222 | 我将这个密文存储在网络服务上,但现在我不需要保存这个密钥了。 223 | 相反,我只需要记住我的口令,每当我需要我的密钥时,我就可以从密钥派生函数中重新构建它。 224 | 问题:是的,这是一个好问题。 225 | 问题是,密钥派生函数是否足够慢,以防止暴力破解?答案是,这取决于你的口令有多长。 226 | 例如,如果你的口令像字符串“password”一样简单,那么它很可能会很快被破解。 227 | 但只要你的口令中有足够的熵,就足够了。 228 | 所以,例如,如果我要上传一些文件到Dropbox,我真的希望它保持机密性,那么一个64位口令,真正具有64位熵的口令,在这种情况下将足够安全。 229 | 这里还有一个快速演示:有一些工具可以让这个过程变得非常容易。 230 | 实际上,这是其中一个练习,但我们可以使用一个名为OpenSSL的工具,将对称密码应用于某个文件。 231 | 例如,我有我的readme文本,readme.md。 232 | 它里面有很多东西,我可以做“openssl aes 256 cbc”,这是一个特定的对称密码的名称,然后我可以说我要将它应用于readme.md,并产生“readme.encrypted.md”,让我们给它起一个名字,然后它会要求你输入一个密码。 233 | 所以,默认情况下,它是在这种模式下工作的,我提供一个口令,它经过KDF处理后生成一个密钥,然后用于加密。 234 | 所以,我会输入一些密码,再输入一遍,然后现在我会生成这个readme.encrypted.md文件。 235 | 如果我查看这个文件,它看起来像垃圾一样,但这正是对称加密的重点。 236 | 它产生了一些密文,应该与随机数据难以区分。 237 | 238 | 当我想要解密这个文件时,我可以运行类似的命令:“openssl aes 256 cbc -d”,表示解密,以readme.encrypted.md作为输入,并将readme.decrypted.md作为输出。 239 | 我可以比较这两个文件,对称加密的正确性属性告诉我这应该是相同的。 240 | 确实如此。 241 | 如果我查看返回值,比较返回值为0,这意味着它们是相同的文件。 242 | 【掌声】所以,关于对称密钥密码学的问题,有什么问题吗?是的,这个特定的命令确实创建了一个新文件,所以它将我们的输入readme.md并产生了这个文件作为输出,这就是该文件的加密版本。 243 | 它并没有改变原始文件,但是我可以删除它,如果我想的话。 244 | 是的,这是一个很好的问题。 245 | 这是我不会详细讨论的东西。 246 | 问题是,我在这里提供了盐值参数,那么它存储在哪里?答案是,它存储在这里的输出中。 247 | 因此,此输出格式存储盐值和实际的输出密文,因此可以用于重构和解密。 248 | 是的,没错。 249 | 它不保留任何数据库或其他东西。 250 | 它是完全自包含的。 251 | 是的,正如John所说,盐并不是密码,密码短语才是这里的秘密。 252 | 好的,让我们回到问题。 253 | 什么是盐?在哈希函数的背景下,加密盐的概念可能最好解释。 254 | 哈希函数的一个常见应用是在密码数据库中存储密码。 255 | 如果我有一个网站,并且有用户的登录,像人们用他们的用户名和密码登录,我实际上不想以纯文本形式存储人们的密码。 256 | 有人知道我为什么不想这样做吗?是的,确切地说,如果出现违规行为,有人获取了您的所有数据,那会怎么样呢?所以,如果泄漏了所有用户的密码,那就真的很糟糕。 257 | 特别是因为许多人在不同的网站上重复使用他们的密码。 258 | 因此,您会看到攻击者侵入一个网站,例如,一段时间前曾经有一个大规模的雅虎泄漏事件,他们发现了所有这些用户名和密码,然后他们试图在Google、Facebook和YouTube等网站上使用这些相同的登录凭证。 259 | 这些人重复使用密码,因此不要存储明文密码。 260 | 因此,您应该使用哈希函数或理想情况下是故意设计成缓慢的密码哈希函数来存储散列密码。 261 | 一旦攻击者意识到人们开始存储散列密码,他们开始构建称为彩虹表的东西。 262 | 263 | 人们开始意识到人们开始存储哈希密码之后,攻击者开始做的一件事情就是构建所谓的“彩虹表”。 264 | 他们采用了一种生成大型密码列表的方法,例如,这些密码的模式是什么样子的。 265 | 例如,获取所有字典单词,获取所有长度为零到八的字符串等等,然后对它们进行哈希,并生成一个将哈希映射回它们的原像的大型数据库。 266 | 因此,鉴于哈希函数的输出,您可以直接在这个数据库中查找“哦,与此输出相对应的输入是什么?”人们已经为相当大的密码数据库构建了这些工具。 267 | 因此,您可以作为一种防御措施采取的一件事是,不仅要在数据库中存储密码的哈希值,而且要计算所谓的“盐值”。 268 | 这是一个大的随机字符串。 269 | 然后,您将存储在密码数据库中的是盐,它不是真正的秘密,您可以将其与附加到其后的密码哈希值一起存储在密码数据库中。 270 | 这有什么用处呢?这种盐是每个用户的随机唯一值。 271 | 因此,如果有人在一个网络服务上使用密码“safe password one two three”,如果您只存储密码的哈希值,则哈希在两个网络服务上都是相同的,对吗?因为这个哈希函数是一个确定性函数。 272 | 但是,现在,由于我们使用了这个随机化的盐值,我们存储了密码哈希值加上盐。 273 | 因此,即使某些人在多个网站上使用相同的密码,这个东西在两种情况下看起来都不同。 274 | 它使这些大型数据库,将这些短密码或哈希输出映射回它们来自的短密码,不再有用。 275 | 当您拥有盐味密码时,您需要为每个用户执行暴力攻击一次,一旦找到他们的盐值,就不能再使用这个大型预先计算的数据库了。 276 | 这回答了什么是盐的问题吗?那么,这个盐参数就是与之相关的。 277 | 看看,我们谈论过的任何问题吗?好的,那么我要抹去这个,然后我们最后要谈论的是密码学中最令人兴奋的发展之一。 278 | 它发生在相当长的时间以前,但仍然是一个非常酷的概念,称为对称密钥密码学。 279 | 这实际上是一种想法,使今天您使用的任何安全性和隐私相关功能都成为可能,例如,当您需要输入www.google.com/mapmaker时,密码学会作为其中的一部分。 280 | 这看起来与对称密钥密码学所讲的非常相似,但有一个区别。 281 | 有一个密钥生成函数,同样是随机的,但不是生成单个密钥,而是生成一对密钥,其中一个被称为公钥,另一个被称为私钥。 282 | 然后可以使用它们进行加密和解密,方式与对称密钥密码学类似,但这些不同的密钥现在具有不同的用途。 283 | 因此,我们有一个加密函数,它接受明文(我在这里写成P),并且接受公钥,并产生密文。 284 | 然后我有一个解密函数,它接受我的密文和私钥,并将明文还原回来。 285 | 与前面所讲的两个属性类似,仅凭密文,我们无法解密出明文,除非我们有私钥。 286 | 287 | 然后,我们有一个明显的正确性属性,如果我们用公钥加密某些东西,然后拿着那个密文尝试用相应的私钥来解密,输出两个不同的东西,那么最终我将得到相同的结果。 288 | 所以这非常类似于上面所说的,但有一个扭曲的地方,我们有两个具有不同功能的不同密钥。 289 | 这个公钥实际上可以被制作成公开的,这很棒,正如名称所示,任何人都可以在互联网上使用类似的加密系统,发布一个公钥供任何人查看,但保持私钥的机密性。 290 | 然后我有了这个有趣的属性,任何人都可以使用我的公钥加密任何内容并通过互联网将其发送给我。 291 | 然后我可以使用我的私钥解密它,只要我的私钥保持机密,我的公钥对于任何人都可以使用是无关紧要的。 292 | 这就是不对称性的来源所在。 293 | 之前,我们处于这样一种情况,假设我在互联网上,但你没有和我面对面交谈,你想通过一些未加密的频道将数据发送给我,并且你想使用对称密钥加密。 294 | 我们需要一种方式事先交换密钥,以便您可以使用密钥加密一些纯文本,并将那个密文传输给我,以便我可以使用该密钥解密它。 295 | 在对称密钥加密中,如果密钥是公开的,那么游戏就结束了,任何人都可以解密你的东西。 296 | 而在非对称密钥加密中,我可以拿出我的公钥并将其张贴在互联网上的公告板上,您可以查看并使用一些内容来加密它们,然后发送它们,这将是完全可以的,您只能使用私钥来解密它。 297 | 所以一个有用的比喻是将这些数学思想与物理锁进行比较。 298 | 你的房门可能有一个锁,你可以用钥匙将其旋转以锁门或旋转另一个方向以解锁门。 299 | 因此,有一把单一的钥匙,它既可以锁门也可以解锁门。 300 | 但现在考虑这种替代构造,如果我想让你能够向我发送一条消息并通过互联网发送,而且你和我之间不需要交换钥匙的方式。 301 | 我可以买一个盒子,你可以把信放在里面,然后你可以关上盒子。 302 | 我可以买一个锁眼,我可以打开锁眼并把它给你。 303 | 你可以在自己方便的时候把你的信息放在盒子里,然后拿起这个打开的锁眼,把它固定在盒子周围,然后将其发送给我。 304 | 然后我可以用我的钥匙打开它。 305 | 所以你能看到这种不对称性,与我用来打开我家门的钥匙不同,同一把钥匙可以开关门。 306 | 相反,我给你这个打开的锁眼,你有能力关闭它但无法打开它。 307 | 在你关闭它之后,我可以使用我保密的钥匙来打开它并取出里面的东西。 308 | 也许这个比喻有帮助,也许没有。 309 | 数学构造如果适合你,那么它就可以工作得很好。 310 | 311 | 关于对称密钥加密和解密以及它与对称密钥加密的关系有什么问题吗?在我们讨论这个想法的应用之前,我将谈论对称密钥密码学中的另一组概念。 312 | 这些加密系统为您提供了另一组与加密和解密相关的工具,称为签名和验证。 313 | 这在某种程度上类似于现实世界,我可以得到一份文件并用我的签名签署它。 314 | 除了现实世界的签名相对容易伪造外,这些签名是相当难伪造的,因此更有用。 315 | 签名方案是什么样子的?有一个函数sign,它需要一些消息和私钥,注意这是私钥,而不是公钥,它会生成一个签名。 316 | 然后还有另一个函数verify,它接收消息、签名和这次是公钥,它会返回一个布尔值,告诉我签名是否正确。 317 | 然后这一对函数具有以下性质,这些性质是从物理签名直觉中产生的,即如果没有私钥,很难为任何消息产生一个签名,使得您可以将消息和签名以及公钥提供给verify函数以使其返回true。 318 | 在高层次上,它很难被伪造。 319 | 当然,没有私钥是很难伪造签名的。 320 | 然后有明显的正确性属性,如果您使用公钥签署了一个东西,然后尝试使用相应的私钥进行验证,它将返回一个好的验证结果。 321 | 因此,这是不对称密钥加密系统可以做的两种不同的事情。 322 | 您可能听说过的不对称密钥加密系统的例子是RSA。 323 | RSA由许多人设计,其中一人是罗恩·里维斯特(Ron Rivest),他是这里的教授。 324 | 实际上有很多很有趣的不对称密钥加密应用,您可能需要花上几天时间来谈论这些应用,但其中一些例子是电子邮件加密。 325 | 我们谈到了发送消息的一些事情。 326 | 通过不对称密钥加密,您可以在网上发布公钥。 327 | 我认为一些教练在他们的网站上发布了PGP公钥。 328 | 例如,如果您访问我的网站或John的网站,您将找到一个公钥。 329 | 然后您可以发送给我们一个加密的电子邮件。 330 | 即使该消息通过Gmail或其他电子邮件服务传递到我的T的邮件服务器,如果有攻击者窥探消息,他们也无法理解其内容,因为它们都被加密了。 331 | 这非常酷,因为你可以不需要亲自见面交换密钥,而是可以在网上找到我们的公钥,然后发送加密邮件给我们。 332 | 这个过程在对称密钥系统中可能需要更多的交换。 333 | 此外,非对称密钥加密还可以用于私人通信。 334 | 335 | 举手如果你使用过类似信号或电报或者WhatsApp的应用程序,这些私人通信应用程序也使用非对称加密技术建立私人通信渠道。 336 | 基本上,每个人都与之相关联的都是一对密钥,因此你的设备已运行密钥生成功能并生成了一个公钥和一个私钥,自动将你的公钥发布到互联网上。 337 | 例如,如果你使用信号,你的公钥就在信号服务器上,当有人想要联系你时,他们的手机可以查找你的公钥,检索它,一旦检索到你的公钥,他们就可以为你加密信息。 338 | 这是他们算法的一种近似方式,但从高层次来看,这就是正在发生的事情。 339 | 非对称密钥密码的另一个有趣应用是我们之前谈到的,就是确保你从互联网上下载了正确的软件。 340 | 非对称密钥密码可用于签署软件发布,这是一些人们为了确保从互联网上下载的软件是来自正确的人所做的事情。 341 | 开发人员会尝试签署他们的软件,以便你可以确保从互联网上下载的任何东西都是来自正确的人所发出的。 342 | 我们在git的讲座中谈到了你可以使用git做的所有有趣的事情。 343 | 我们没有涉及的是git中签名相关的功能。 344 | 所以git有提交,你可以将一些东西与提交关联起来,这被称为标签。 345 | 从高层次上看,你基本上可以将一个git提交与一个签名相关联,将你的公钥绑定到此提交中,然后任何拥有你的公钥的人都可以使用提交和你的公钥,以确保提交上有一个合法的签名。 346 | 让我去我的一个随机存储库看一下。 347 | 我可以查看与存储库相关联的一些标签。 348 | 如果我查看与该标签相关联的原始数据,它具有一些元数据,然后是一段ascii编码的信息块,我可以使用“git tag -v4 verify”命令,以确保哦,这是一个好的签名来自这个人碰巧是我,所以我签署了软件发布,这样任何从互联网下载软件的人都可以确保他们实际上得到了真实的副本。 349 | 是的,问题。 350 | 所以问题是验证函数具体是在做什么或者它检查什么?如果你想数学上了解具体是什么,可以在本讲座之后找我交流。 351 | 352 | 从 API 的角度来看,这里的签名和消息只是一组字节数据。 353 | 这些数据的设计基本上是这样的:对于某个特定的公钥,比如我的公钥,如果你没有我的私钥,就不可能找到第二个参数使函数返回 true。 354 | 你可以将它类比为签署一份文件。 355 | 就像你不知道如何伪造我的签名一样,我可以在任何纸张上签名,然后任何知道我的签名样式的人,可以查看我的文件,验证签名正确。 356 | 但没有私钥的人无法为任何特定消息产生一个使此函数返回 true 的签名。 357 | 有任何相关的问题吗?我是否需要以其他方式解释,或者这讲得通?那么对于软件签名或对称密钥加密中讨论的其他几种应用程序,有什么问题吗?好的,最后我想谈谈密钥分发。 358 | 这是非对称密钥加密的一个有趣的副作用。 359 | 它可以实现许多有趣的功能,比如我可以在互联网上发布我的公钥,你可以找到它并给我发加密邮件。 360 | 但你怎么知道找到的公钥实际上是我的公钥?这似乎存在一个引导问题,对吗?所以,你可能会采取几种不同的方法来解决这个问题。 361 | 一种是有点糟糕的解决方案,但它可以解决许多密码学问题。 362 | 这种方法是在带外交换信息,也就是你想给我发加密邮件,我们在下课后就直接谈,我会给你一张纸条上面写着我的公钥,由于你亲自与我交谈,你知道这确实是我的公钥,而不仅仅是有人黑进我的网站,放上一些随机数。 363 | 这样可以解决问题,但不太优雅。 364 | 还有几种不同的方法,不同的应用程序使用不同的方法。 365 | 所以,使用 Signal 的人们,你们有没有遇到过"安全号码"这个短语?对于 Signal,他们有一种交换公钥的方法,就是通过 Signal 服务器。 366 | 运行 Signal 服务的人只需在其服务器上维护一个从电话号码到公钥的映射。 367 | 当我说"我要给这个号码的人发消息"时,我的手机会从互联网上检索他们的公钥,然后为该公钥加密消息。 368 | 现在,有人看到这个设置有什么问题吗?是的,正是如此。 369 | Signal 服务器是一个故障点,因为如果 Signal 服务器给我错误的公钥,比如假设 Signal 只是生成了一对新的密钥并给我他们的公钥,现在他们可以读取我所有的消息,他们甚至可以坐在我和我的朋友之间,透明地解密我发送给他们的消息,然后重新加密并发送到最终目的地。 370 | 基本上,我需要某种方法来验证我获得的公钥,因此 Signal 有一个解决方案,也只是将问题推给了带外密钥交换。 371 | 你可以和某个人见面,他们有一个稍微精简的流程,在屏幕上显示 QR 码,你拿一部手机拍摄另一部手机屏幕的照片,反之亦然,现在你已经在人面前交换了公钥,从那时起。 372 | 如果你没有我的私钥,任何消息都无法找到使该函数返回true的第二个参数。 373 | 374 | 这有点像签署一份文件。 375 | 就像你不知道如何伪造我的签名。 376 | 我可以在任何一张纸上签名,然后任何知道我的签名长相的人都可以查看我的文件,你可以说:“是的,这个签名是真的”。 377 | 但是除了拥有私钥的人,没有人能够为任何特定的消息提供第二个参数,使得这个函数返回真。 378 | 如果还有相关的问题需要解释,或者有其他的解释方式,或者这样解释是否有意义?那么,对于任何有关签署软件或非对称密钥加密等少数应用程序的问题,您有没有任何问题?好吧,最后我想谈论的是密钥分发。 379 | 这是非对称密钥密码学的一种有趣的副作用。 380 | 它使许多有趣的功能成为可能,例如,我可以在互联网上发布我的公钥。 381 | 你可以去找到它并给我发送加密邮件。 382 | 但是,你怎么知道找到的公钥实际上是我的公钥呢?似乎这里有一个引导问题,对吧?因此,你可能会采取几种不同的方法来解决这个问题。 383 | 其中一种是有点糟糕的解决方案,但这个东西解决了许多加密问题,那就是通过离线信息交换。 384 | 这意味着,如果你想给我发送加密邮件,我们只需要在课后和我谈谈。 385 | 我会在一张纸上给你我的公钥,因为你是亲自和我交谈的,所以你知道这实际上是我的公钥,而不仅仅是有人入侵了我的网站并贴上了一些随机数字。 386 | 这解决了问题,但不是最优雅的方法。 387 | 有几种其他方法是不同的应用程序使用的。 388 | 所以,你们中使用Signal的人,你们是否遇到过“安全号码”这个短语,比如“验证你的安全号码与某某人”?那么,对于Signal来说,他们有一种交换公钥的方式是通过Signal服务器。 389 | 运行Signal服务的人只是在他们的服务器上维护了一个从电话号码到公钥的映射。 390 | 当我说:“哦,我想用这个号码给这个人发消息”,我的手机就会去互联网上检索他们的公钥,然后为那个公钥加密消息。 391 | 现在,有没有人看到设置的问题?是的,完全正确。 392 | 信号服务器是失败的瓶颈,因为如果信号服务器提供给我的公钥不正确,例如,假设信号服务器生成了新的密钥对并给我他们的公钥,现在他们可以读取我所有的消息。 393 | 他们甚至可以坐在中间透明地解密我发送的消息,然后重新加密并发送到最终目的地。 394 | 基本上,我需要一种认证我得到的公钥的方式。 395 | 因此,信号有一个解决方案,也是将问题推给线下密钥交换的方法。 396 | 你可以与某个人见面,他们有一个稍微简化的流程,可以在屏幕上显示QR码。 397 | 你拿一部手机拍下另一部手机屏幕的图片,反之亦然,现在你已经在人面前交换了公钥。 398 | 从那时起,你已经启动了加密的端对端通信。 399 | 它还有一个问题或方法,即固定公钥。 400 | 一旦你知道一个特定的电话号码有一个特定的公钥,你的手机就会记住它,如果发生更改,它就会向你发出警告。 401 | 402 | 然后还有几个解决这个问题的方法。 403 | PGP是一个曾经流行过的解决方案,它有一个信任网络的概念。 404 | 就像,我信任我朋友信任的人。 405 | 所以如果约翰和我的教授进行了一次超出带宽的交流,那么我可以向我的教授发送电子邮件,因为我知道约翰信任我的教授,我信任约翰。 406 | 所以你可以通过这种方式建立信任链。 407 | 这是一种有趣的方法。 408 | 还有另一种模型,叫做最近出现的一种工具叫做key base,这是一个非常好的...哎呀,有一个叫做keybase.io的网站,他们有一个非常有趣的解决方案,叫做社会证明。 409 | 比如说,你可能在Facebook和Twitter上有你的朋友,攻击者很难同时入侵你朋友的Facebook账号、Twitter账号以及Hacker News账号等等。 410 | 因此,有一种有趣的方式,将公钥绑定到一组社交身份上,这样一旦你信任与你朋友相应的社交身份数量,就可以检索到公钥。 411 | 如果你想要更详细地了解这些内容,我们在讲义中提供了链接。 412 | 这就是我们的安全和密码学讲座,明天的讲座将涉及到一些你的教练们认为有趣的话题。 413 | 希望明天在讲座上见到你们。 414 | 如果有任何问题,我会在课后呆上几分钟。 415 | 好的,约翰,如果你要离开,随意离开,但我想没有人在我们之后使用这个教室。 416 | 我要谈论另一个有趣的话题。 417 | 约翰提出了非对称密钥加密速度慢,对称密钥加密速度快的事实。 418 | 因此,在实践中,你不会仅仅使用对称密钥加密。 419 | 通常情况下,它用于引导你正在使用的更复杂的协议。 420 | 你可能想使用对称密钥加密来签名加密邮件,对吧?我们已经讲过了这个例子。 421 | 实际上的工作原理并不像我们对非对称密钥加密的简单解释所猜测的那样。 422 | 你不能只是使用上面的加密函数就结束了。 423 | 在实践中,你使用混合加密来使用对称密钥和非对称密钥加密的组合。 424 | 你所要做的是使用混合加密技术,结合对称密钥和非对称密钥加密。 425 | 这里,我会画一个大的块状图。 426 | 你需要先取出你要发送的信息M,然后使用我所拥有的公钥进行加密。 427 | 但是,与其将这两个信息直接通过上述加密函数加密,实际上,你需要使用对称密钥生成函数来产生一个对称密钥。 428 | 429 | 好的,我会在开头加上“对称”这个词,以便我们可以将其与公钥密钥生成函数区分开来。 430 | 然后我将这两个东西通过对称加密传递给我的加密盒子。 431 | 这将产生密文,现在将其发送到接收者那里。 432 | 抱歉,这个加密后的内容对接收者来说并没有什么用处,因为它是使用本地计算机上运行的函数产生的密钥K进行对称加密的。 433 | 因此,我需要某种方式将此传递给实际上用于解密邮件的人。 434 | 因此,我将此内容拿出来,而且可能这个邮件很大,所以我使用对称加密将其加密。 435 | 但是这个密钥很小,可能只有256位,所以我可以使用公钥进行对称加密,这样就可以生成一个加密密钥。 436 | 使用相应的私钥解密该密钥,就可以重构它。 437 | 这是在发送者端进行的。 438 | 现在,接收者获得这个密钥,将其倒序进行以下步骤:首先使用非对称解密使用对应于所发布的公钥的私钥重构用于对称加密盒子的密钥,然后使用该密钥进行对称密钥解密,以获取原始消息。 439 | 因此,这是对称和非对称密钥加密如何在实践中结合的有趣示例。 440 | 问题:那么问题是,你会使用相同的对称密钥生成器吗?是的。 441 | 因此,您需要事先协商在此处使用哪个盒子。 442 | 例如,您可能会说,我将在这里使用AES 256 GC,但这是一个众所周知的函数,它是公开的。 443 | 攻击者可以知道此函数的所有参数。 444 | 这是攻击者不知道的唯一秘密。 445 | 其他问题吗?是的,那是一个非常好的问题。 446 | 什么样的数据值得加密?我认为这取决于您的威胁模型。 447 | 你关心什么样的攻击者?你想保护什么?因此,您可能认为您根本不在意,并且您与任何人的通信都可以公开。 448 | 我可能愿意将我与每个人的所有对话公开发布到互联网上供所有人查看。 449 | 另一方面,也许你正在从事一些类似安全敏感的工作,为美国政府签订合同,开发一些敏感军事项目。 450 | 如果你在旅行中通过公共互联网发送这些信息,你可能希望非常确定没有窃听者或其他人能够看到你发送的内容,并且你发送的信息确实到达了正确的位置,并且接收方能够验证该信息确实来自你。 451 | 因此,根据你的情况,你可能会担心各种不同类型的对手,从试图破解网站的随意的脚本小子到国家级攻击者,你需要不同类型的技术来防御不同类别的攻击者。 452 | 还有其他问题吗?好的,希望明天能看到一些对于 John、Jose 和我感兴趣的随机的事物的集合。 453 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture10_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 大家好,能听到我的声音吗? 2 | 好的,欢迎回到你们的计算机科学教育中缺失的学期。 3 | 今天我们的讲座主题是“杂项”。 4 | 这将是一些我们讲师认为有趣但又不足以成为单独讲座的主题的组合。 5 | 但是它们都是我们希望你们知道的一些特定主题,因为它们可能非常有用。 6 | 由于我们不会详细探讨这些主题,如果你对它们更感兴趣,可以随时在讲座结束后或讲座期间向我们提问。 7 | 所以我想先谈谈键盘重新映射。 8 | 到现在为止,你可能已经意识到我们鼓励你使用键盘作为主要的输入方法。 9 | 例如,当我们进入编辑器讲座时,Vim 的一个主要思想就是尽可能多地使用键盘。 10 | 这样你就不必依赖鼠标,因为使用鼠标是慢的。 11 | 事实上,你的键盘,就像电脑中的许多其他东西一样,都可以进行配置。 12 | 而且配置是值得的,因为许多默认设置可能不是最优的。 13 | 最简单的修改方法就是重新映射键。 14 | 例如,在编辑器讲座中我们提到,大写锁定键是一个非常好的键,因为它位于主键盘区,而且很大,但它并没有什么用处。 15 | 你可能会意识到你不常使用大写锁定键。 16 | 因此,你可以将大写锁定键重新映射为更有用的键,如 Escape(如果你是 Vim 用户)或 Control(如果你是 Emacs 用户)或其他实用的重新映射,如 F 功能键或 Print Screen。 17 | 例如,你可以将它们重新映射为媒体键,这样当你按下 Print Screen 时,你可能不需要那么频繁地截屏,但你可能需要播放或暂停音乐。 18 | 几乎每个操作系统都有一些工具可以用来配置这些功能。 19 | 我不会详细介绍,但在注释中列出了其中一些。 20 | 哦,是的,你可以使用键盘重新映射来执行更复杂的组合操作。 21 | 你可以将一些键的组合映射到某些操作上。 22 | 例如,我有一些键盘重新映射,每当我按下 Ctrl+Enter 时,就会打开一个新的终端窗口。 23 | 24 | 因为这是我经常做的事情,并且在Mac上默认情况下没有绑定按键来执行该操作,或者Ctrl + Shift + Enter将打开一个新的浏览器窗口,这是我每天都要做的另一个操作。 25 | 所以我不必拿起鼠标去Chrome执行它。 26 | 你也可以重新映射来执行操作。 27 | 如果你不想一直输入你的密码......不好意思,不是你的密码,是你的电子邮件或密码,或者例如你的MIT ID,你可能不记得它,那么你可以有一个键盘组合键来执行粘贴文本的操作。 28 | 最后,还有更多...现在看起来你只需要执行“这是你按下的键和这是发生的动作”的功能。 29 | 但实际上有更复杂的键盘组合键,当你不断学习时,你会发现你可以执行键盘序列。 30 | 所以例如当我们处理tmux时,在tmux中有这个概念,首先你按下Ctrl + A或Ctrl + B,就像你按下一些前缀然后一些其他的键,那意味着某些东西。 31 | 很多这些软件都允许这样做。 32 | 例如,在我的键盘上,因为我根本不使用Caps Lock,但偶尔我的本科生有一些依赖Caps Lock更改模式的软件。 33 | 然后我可以快速连续按五次Shift键,然后处于中间的解释这些命令并重新映射到其他命令的软件将为此发送一个单独的Caps Lock命令。 34 | 更多的例子是我提到你可以使用你的Caps Lock键映射到Escape或Control,但实际上你可以重新映射它们两个。 35 | 所以在我的电脑上,当我轻按Caps Lock键时,它被解释为Escape。 36 | 然而,如果我按住它,这个软件可以理解快速按下它和仅仅按住它以与其他键组合使用的区别,然后在这种情况下它被映射到Control。 37 | 很多这些更高级的配置都得到了许多这些工具的支持。 38 | 正如我所提到的,我们有一个关于Windows、Mac OS和Linux的这些程序的良好默认值的简短列表。 39 | 这个话题有什么问题吗?好的,现在我要讲解一个与键盘映射无关的话题[笑声]。 40 | 我们将在本讲座中看到许多这些不相关的过渡。 41 | 这就是“守护进程”的概念。 42 | 所以你可能......也许如果你不熟悉这个领域,这个概念看起来很陌生,但你对守护进程这个概念应该已经很熟悉了。 43 | 大多数计算机在运行时会启动和运行一些软件,就像我们一直在看到的那些命令一样,比如输入“ls”,就是调用“列出文件列表”命令。 44 | 45 | "ls"命令执行是因为你要求它执行,然后它就完成了。 46 | 但是许多其他程序只是作为后台进程运行,它们只是在后台执行并等待事件发生或启用计算机的某些功能。 47 | 这些进程的示例可能包括像网络管理器这样的部分,或者像管理显示的计算机部分。 48 | 您会发现,守护进程启用的大多数内容通常是以'd'结尾的程序。 49 | 例如,当您通过SSH连接到计算机时,接收计算机必须具有SSH守护进程,该程序称为"sshd",如果此程序未运行,则无法通过SSH连接到计算机。 50 | 如果该程序正在运行,则该程序将正在侦听,当您SSH到该服务器时,一些传入请求将进入计算机,计算机将将其发送到在后台运行的该守护进程,然后该守护进程将检查您是否有授权,如果有,则将启动一些登录Shell,以便您可以开始执行。 51 | 不同的操作系统处理此问题的方式有所不同,但它们都有某种响应这些较小守护进程的系统守护进程。 52 | 在我们为许多示例选择的Linux中,您正在使用的工具是"systemd"。 53 | 首先,系统守护进程将启动许多这些进程,如果您使用"systemctl"命令,则可以检查不同守护进程的状态,您可以检查哪些正在运行,可以要求它启动进程或停止它们。 54 | 这是一种一次性的操作。 55 | 您还可以启用它,然后禁用它们,这将告诉系统在启动时运行它们或停止启动它们。 56 | 更有趣的是,您可以配置自己的"systemd"单位。 57 | 到目前为止,所有示例都是计算机必须执行的许多内容,但是假设您想运行Web服务器。 58 | 一个解决方案是,您可以每次启动计算机时打开tmux会话,然后执行命令,但这不是计算机期望守护进程运行的方式。 59 | 计算机期望守护进程运行的方式是使用某种"systemd"单位。 60 | 这是一种告诉"systemd"如何执行此进程的配置。 61 | 这个的一个简单例子是: 62 | 这里发生的事情是我们正在向"systemd"描述需要执行此程序的步骤。 63 | 这个例子只是运行一个简单的Python应用程序,你可以将其视为使用某些Python Web服务器库实现的Web服务器。 64 | 我们在这里说的是这个描述,我们说在某个之后,像这样很重要,"Systemd"有一个必须启动的服务列表,像所有这些守护进程都必须启动,但是可能存在这些守护进程之间的依赖关系,所以我们在这里说"不,只有在网络设置完成后才能启动此服务",因为否则,如果我无法侦听网络端口,你甚至如何尝试配置我们的Web服务器呢?然后我们定义了应该运行此服务的用户,因为你可能想要将其作为自己的用户运行,或者可能是其他用户,或者可能是root用户在运行此服务,然后定义了要运行的命令和在哪个目录下运行。 65 | 当你有这个时,可能会有一些小的角落情况需要调试,但这是核心思想,可以非常有用地自动化后台运行进程的过程。 66 | 对此的一个小侧面说明是,如果你只想定期运行一个命令,比如每天早上我都想在我的计算机上做一些事情,你可以编写一个守护进程,它只做一些事情然后睡觉一天,但实际上Linux和Mac OS已经有了一个这样做的守护进程,称为"crond","crond"将采用另一种类型的配置文件,你可以说哦,我想在每天上午8点运行一个命令,或者我想每5分钟运行一个命令,它将只检查此事件并执行它。 67 | 对于许多事情,你会发现已经配置了相应的守护进程。 68 | 有关守护进程的任何问题吗?[学生含糊不清地讲话]问题是是否有一个计算机上的文件夹,其中包含所有这些配置文件?是和不是,这些配置文件中的一些位于几个不同的文件夹中,具体取决于它们是系统守护进程还是用户守护进程。 69 | 在这里,您可以看到在第一行的位置是将此放置在系统守护进程中以便其被识别为已安装的位置,但是如果您只想列出正在运行的所有守护进程,则例如在Linux中,您只需执行“systemctl status”,它将打印出所有系统的树状结构,以及哪个守护进程是由哪个其他守护进程产生的,其中许多直接由“systemd”生成。 70 | 下一个话题将是用户空间的文件系统,所以,对此进行快速介绍的事实是,每当您使用现代操作系统时... 哦,对不起,还有一件事,每当您使用现代操作系统时... 71 | 你不必局限于特定的文件系统。 72 | 现代系统相当模块化,例如在Linux中有不同的文件系统可供选择。 73 | 这是因为内核是运行大部分操作系统的程序,有一些模块知道如何与文件系统进行交互。 74 | 通常,当你执行像"touch foobar"这样的命令时,这是在用户级别进行的,然后通过内核级别,并有一层检查在这个操作发生的位置,以确定它属于哪个文件系统。 75 | 例如,你将拥有多个磁盘,每个磁盘都有不同的文件系统,因此内核必须确定要使用哪个文件系统操作,并说这个文件可能在一个"Ext4"文件系统中,这是最常见的Linux文件系统之一。 76 | 那么,每当你执行"touch foobar"时,内核会听到并试图找出它属于Ext4文件系统,然后执行创建文件的相关指令。 77 | 然而,这种系统的缺点是现在我不能拥有定义如何创建文件的用户代码,在某些情况下这可能会很有用。 78 | 比如说,我想拥有一个文件系统,每次有人创建文件时,它会给我发送一封电子邮件,这样我就可以知道人们正在创建这些文件。 79 | 但是在这里我无法修改内核来添加这个功能。 80 | 解决这个问题的方法叫做"FUSE",FUSE是一种在用户空间中拥有文件系统的方法。 81 | 因此,如果这个文件不是在Ext4中,而是在FUSE文件系统中,FUSE将把这个操作转发到用户调用的某个其他部分,该部分将说"哦,创建这个文件"。 82 | 在这里,我可以拥有发送电子邮件给我的代码部分,告诉我"哦,这个文件已经被创建了"。 83 | 如果你仍然想创建该文件,它可以把请求转发回来,做一些更多的内核操作。 84 | 这似乎并不是很实用,但这只是理论上的。 85 | 在实践中,这很有用,因为现在你可以在尝试执行文件系统操作时执行任意动作的用户级代码。 86 | 一个非常有趣的例子是"FUSE" 87 | 在一个SSHFS FUSE文件系统上,无论你尝试创建、打开、读取、写入一个文件,它都会通过SSH连接到远程服务器,而不是尝试在本地文件上执行操作。 88 | 因此,如果我在这里尝试创建一个文件,它将使用SSH连接将该操作转发到远程系统,然后在那里执行。 89 | 因此,在我的本地计算机上,在运行的其他程序中,有一个路径看起来好像它在这里,但是对路径执行的所有操作都被转发到远程文件系统。 90 | 基于这个想法,你会在笔记中找到一些例子,并在线上找到更多,说明人们如何利用这种能力来创建相当有趣的文件系统。 91 | 例如,如果你不关心SSH,因为你使用Dropbox或Google Drive,那么人们已经实现了FUSE文件系统,可以在本地挂载,每次你尝试在本地执行操作时,实际上它会去这些云存储提供商之一,所以你也可以使用像Amazon S3或Google Cloud Storage这样的服务,它们没有像Dropbox或Google Drive那样的同步UI系统。 92 | 这种方法的另一个应用与远程操作无关,就是加密文件系统。 93 | 你可能有一个文件系统,每次你尝试写入一个文件时,你都会尝试以明文形式写入它,但它会捕获该操作,动态地加密它,然后将其保存为常规文件在你的文件系统中,但实际上是加密的。 94 | 一旦你卸载文件系统,一旦你断开FUSE连接,你的计算机上只剩下一些加密的常规文件。 95 | 我想要讨论的最后一个主题是备份和一些关于它们的良好做法。 96 | 主要的想法是,对于你关心的每个文件,如果你没有备份它,如果你没有存储它的备份,你几乎可以在任何时候失去它。 97 | 有许多不同的故障场景。 98 | 其中之一就是硬件故障。 99 | 所以你的硬盘可能在任何时刻都会出现故障。 100 | 因此,如果你只是在同一个驱动器上复制你的文件,那是没有用的。 101 | 如果你的硬盘故障了,文件就丢失了。 102 | 如果你在外部驱动器上进行备份,但是如果你把所有东西都存储在你的家里,而你的家着火了......这虽然不太可能,但如果发生了,你就失去了所有的数据。 103 | 104 | 所以问题是电脑中是否有一个文件夹包含了这些备份,因此你需要有一个离线备份的解决方案。 105 | 还要注意的一件事是同步或镜像选项不是备份。 106 | 所以像我之前提到的 Google Drive 和 Dropbox 只会传播你电脑上发生的任何事情。 107 | 这也适用于硬件镜像,如 RAID。 108 | 它们只是在制作副本。 109 | 如果你不小心删除了一个文件,或者有人恶意删除了你的文件,或者使用某些勒索软件对它们进行加密,那么你可能会有一个副本,但你只有一份无用的数据副本。 110 | 你必须拥有备份运行的解决方案。 111 | 你应该问自己,有什么人需要知道或拥有你的信息才能删除你所有的数据。 112 | 我们在笔记中链接了不同的软件,介绍了如何备份。 113 | 最后我要提到的是备份时,很多时候你只会考虑本地文件,比如所有的照片和报税申报表,怎么备份?但在现代,越来越多的网络应用程序和很多数据只可能存在于一些云服务提供商,比如如果你使用 Webmail,而且你没有将其同步到电脑上,它只存在于提供商的服务器上。 114 | 如果你没有这个数据的备份,并且由于某些原因无法访问该帐户,因为你忘记了密码,被黑客攻击,他们认为你违反了服务条款...所有的数据都会消失。 115 | 因此,你应该寻找一些工具来制作所有这些数据的离线副本,这样你就可以定期备份。 116 | 这个短小的备份部分就到这里了,有什么问题吗?(学生)当你说硬盘随时会出故障时,它会出故障的原因是什么?[不清楚听到的话]比如说我把外接硬盘放在我父母家里或者其他地方,我的电脑在这里,这样就足够了吗?还是任何硬盘都可能随时失效?(Jose)任何硬盘都可能随时失效,我们没有一个完美的解决方案。 117 | 不同的媒体有不同的故障率,在网上有非常好的统计数据,例如,旋转硬盘的故障率比固态硬盘高。 118 | 如果你将硬盘掉落,故障率会更高,但总体上我们没有一个完美的解决方案。 119 | 120 | 关于“这个媒体不会失败”的说法,实际上,像SD卡、固态硬盘、硬盘、CD等都会随着时间而逐渐退化,基本上每种数据都会因此而有可能在任何时刻丢失。 121 | 此外,你还需要知道数据也有可能会损坏,你的硬盘看起来可能没问题,但是有些文件可能已经损坏了,而像Google Drive或Dropbox这样的同步技术会传播这种损坏。 122 | 等到你意识到出现问题时,可能已经太晚了。 123 | (Jon)好的,我们将继续跳跃到不同的主题并讨论API。 124 | 到目前为止,我们一直在讨论如何在本地计算机上更有效地完成任务。 125 | 例如,我想更有效地完成此任务。 126 | 我该如何配置我的编辑器?我该如何使用我的shell?但你应该意识到,通常你也可以与外界集成。 127 | 你日常互动的大多数服务都提供某种API,供你与其存储的数据或提供的服务进行交互。 128 | 而且通常这些API都有很好的文档。 129 | 如果你查看Facebook、Twitter、Google Drive或Gmail等服务的API,你会发现许多接口都可以供你从本地机器上使用这些服务。 130 | 真正有趣的是,你通常可以将此与我们迄今为止在课堂上讨论的一些内容相结合。 131 | 例如,在数据整理的讲座中,我们介绍了如何创建这些管道以从某些具有不同格式的源中提取数据。 132 | 例如,美国政府提供了一个免费服务,你可以请求任何美国地点的天气预报。 133 | 你所要做的就是请求一个URL,并在其中设置正确的参数,然后获取数据,你将得到JSON,这是一种定义良好的数据格式,你可以解析它,并提取像未来14天的天气预报之类的信息,然后你可以将其导入到shell中,并生成一些方便的别名,以便在终端中打印出任何位置的未来14天天气的方便参考。 134 | 135 | 这些事情你可以相对容易地构建,关于如何构建的说明在笔记中。 136 | 一般来说,当你与这些API交互时,你会使用某种形式的URL,而确切的格式因服务而异。 137 | 但总的来说,URL将包含一些参数集。 138 | 最终,你只需向它们发出一个Web请求,然后以某种格式获取数据。 139 | 你应该知道的一个命令是“curl”。 140 | 所以curl是一个你调用的程序,你给它一个URL。 141 | 它只是获取该URL并把响应返回给你。 142 | 你对响应做什么,完全取决于你。 143 | 也许你会通过像“jq”这样的程序进行管道处理。 144 | 所以jq是一种JSON查询工具,可以接收JSON格式的数据,然后编写查询以提取你感兴趣的数据。 145 | 这是你可以使用这些工具提取你感兴趣的数据的一种方式。 146 | 这些服务中的一些还需要你以某种方式进行身份验证。 147 | 例如,如果你想与Facebook API交互,你需要拥有一些认证令牌,证明你是Facebook关心的那个人。 148 | 否则,他们不能说你是否被允许以某个用户身份创建帖子。 149 | 很多时候,这些东西将使用OAuth,虽然并不总是这样。 150 | 你应该查看你关心的每个服务的文档。 151 | 但总的来说,你将从服务中获得一些秘密令牌,你必须在你发给它们的请求中包含它们。 152 | 无论是在URL中还是在附加的Web头中,你也可以通过curl发送它们。 153 | 但要记住,这些令牌是秘密的。 154 | 它们是你的用户的另一种表现形式,任何拿到它们的人都可以假装是你。 155 | 他们可以用那个令牌做你能做的一切。 156 | 所以要记住这一点,不要把它们放在你的“dotfiles”中,然后把它们推到GitHub上。 157 | 这会让你陷入麻烦。 158 | 你应该把它们当作密码来考虑。 159 | 在线上也有一些非常好的工具,可以将服务集成在一起。 160 | 有一个叫做“If This Then That”的服务,它基本上提供了对一堆不同的服务的集成,并允许你将它们链接在一起,并且如果你愿意,也可以部分地在本地访问它们。 161 | 这是值得研究的东西。 162 | 163 | 如果你想以更高效的方式与特定服务进行交互,有关API的问题请问?好的,我们完全切换话题,讨论命令行参数。 164 | 命令行工具有很多,大多数都需要不同的参数,因为它们执行不同的操作。 165 | 我们已经谈到过查看命令的man页面,这会告诉你如何使用这个特定的命令,你可以给它什么样的标志和选项,以及当你调用它时它实际执行了什么。 166 | 但是有一些常见的主题是有用的,无论是许多程序使用的参数,还是许多程序都适用的概念。 167 | 第一个我们在命令行环境讲座中已经提到了一些,那就是"--help"标志。 168 | 通常你可以将这个标志传递给程序,而不是运行它,它就会打印有关如何运行这个程序的信息,通常是一种非常简短、压缩的方式。 169 | 类似的一个是"--version"标志,它只打印你正在使用的软件版本。 170 | 如果你正在进行诸如提交错误报告之类的操作,并且想要报告你正在运行的版本,以防该错误已经修复,那么这非常方便。 171 | 通常你也可以使用"-V",它与版本相同。 172 | 但是要检查一下man页面。 173 | 还有一个经常用到的是"--verbose"或"-v"标志,它允许你增加程序的输出,使程序打印更多有关它正在做什么的信息。 174 | 通常你可以重复使用这个标志,比如使用"-vvvvv"来从该工具获取更多的信息。 175 | 如果你正在尝试调试问题,这可能特别有用。 176 | 如果你正在运行"rsync",并且想要知道它为什么决定复制这个文件,或者为什么决定不复制这个文件,那么这种调试输出就很有用了。 177 | 通常还有一个相反的标志叫做"quiet"或"silent",它意味着工具不会打印任何东西,除非出现错误。 178 | 很多工具,特别是那些执行破坏性操作或你无法撤销的操作,提供了所谓的"模拟运行标志"。 179 | 这在命令行中的表示方式因工具而异,但基本上,这个模拟运行模式会运行该工具,但实际上不会进行任何更改,而是只会告诉你如果你没有使用模拟运行,它将会做什么。 180 | 181 | 许多这些工具也有交互模式。 182 | 例如,“rm”和“mv”工具都有交互模式,通常只需要“-i”,但不总是这样。 183 | 当您在交互模式下运行工具时,通常会在即将执行无法撤消的操作时提示您,并提示您确认是否应该执行。 184 | 当我们谈论破坏性工具时,其中许多默认情况下都不递归。 185 | 如果您尝试删除一个目录或对一个完整的目录进行操作,则它们不会继续进入该目录内部的文件。 186 | 原因是您可能会意外地删除整个硬盘,这似乎很糟糕。 187 | 因此,对于许多这些工具,它们都有一个“recurse”标志,通常为“-r”,但并非总是如此,它让它们向下遍历树以深入了解,但您需要选择使用此行为。 188 | 例如,“rm”的情况就是如此,这也是“cp”的情况。 189 | 在许多工具中,当它们要求您提供文件名或路径时,我们在数据整理讲座中也稍微谈到了这一点,您通常可以只给出一个破折号“-”,这意味着标准输入或标准输出,具体取决于该参数是输入文件还是输出文件。 190 | 如果您正在尝试构建我们之前谈到的数据整理流水线,那么了解这一点非常有用。 191 | 如果您想要传递看起来像是一个标志或选项的东西给一个命令,但您实际上不想将其解释为标志或选项,则有时需要考虑一些问题。 192 | 例如,如果您想删除名为“-i”的文件,该怎么办?是的,如果您编写以下命令...“rm -i”,那么“-i”是一个“rm”的标志,因此当您运行此命令时,“rm”会说“告诉我要删除哪个文件,您没有给我一个文件”,因为它将其解释为一个标志。 193 | 同样,如果您做这样的事情...“ssh某台机器,某个命令,例如,dash r”,这就是说-在SSH上运行命令“foo”,并且我想向“foo”传递“-r”,那么这两个命令将被解释为标志。 194 | 但对于这个命令来说,这可能不是您预期的。 195 | 实际上,在SSH的情况下,它对某些命令有一些奇怪的特殊行为。 196 | 但是,如果您想要某些东西不被解释为标志,则有一种非常简单的方法可以选择退出,那就是使用双破折号。 197 | 198 | 如果你使用双破折号,那么你告诉命令的是,紧跟在其后的内容不应被解释为标志或选项。 199 | 在“rm”命令的情况下,你可以这样做,现在你会看到第一个参数是“--”,然后它会继续读取参数,但不会将它们解释为标志。 200 | 因此,当它遇到“-i”时,它不会将其解释为破折号i标志,而只是作为参数i。 201 | 同样,对于SSH,你也可以这样做,以指示这些都是位置参数,它们不是标志或选项,你不应解释以破折号开头的内容(学生)。 202 | 但是,如果你使用“--version”,它不会触发这个吗?(Jon)不会,这是一个“--”,两侧有空格。 203 | 你有任何关于命令行约定的问题吗?好的,那么让我们谈谈窗口管理器。 204 | 所以你们大多数人习惯于某种拖放窗口管理器,如果你正在运行Windows、Mac OS或Ubuntu,那么这些机器自带的就是窗口,它们在屏幕上部分重叠,你可以拖放和移动它们,调整它们的大小等等。 205 | 这种方式可以正常工作,但它并不是管理计算机窗口的唯一方式。 206 | 实际上,你习惯的是一种浮动窗口管理器,但并不是所有的窗口管理器都是浮动的。 207 | 通常你可以选择其他类型的窗口管理器,它们有不同的行为方式来布置你的桌面。 208 | 一种常见的替代方案是平铺窗口管理器。 209 | 在平铺窗口管理器中,不再有浮动窗口,一切都设置成平铺布局。 210 | 当你启动一个程序时,它的窗口被最大化。 211 | 如果你启动另一个程序,原来的窗口会缩小,新的窗口会占用总桌面空间的一部分。 212 | 除非没有打开任何程序,否则在任何给定的桌面上打开的所有程序都将共享这个空间。 213 | 这看起来有点像之前我们谈到的tmux面板,你可以在不同的方向上将它们分割。 214 | 这种方式的一个好处是你基本上不需要使用鼠标,在不同的窗口之间移动时,有键盘快捷键可以使用。 215 | 有键盘快捷键可以调整窗口的大小或在屏幕上交换它们的位置,这被证明是一种相当高效的管理计算机窗口的方式。 216 | 我不会详细介绍你可能会使用哪种窗口管理器,只要知道这些存在并值得一看就好了。 217 | 218 | 它们可以更加高效地工作。 219 | 关于窗口管理器有什么问题吗?好的,虚拟私人网络(VPN)。 220 | 与之前的主题有完全关联。 221 | 所以虚拟私人网络现在非常流行,这让我感到非常难过。 222 | 目前并不清楚使用虚拟私人网络是否有很好的理由。 223 | 因为你应该意识到虚拟私人网络能够做什么,以及不能做什么。 224 | 在最好的情况下,虚拟私人网络只是一种更改你的互联网服务提供商的方法。 225 | 这是一种让互联网上的流量看起来来自于一个比你实际所在的地方更远的地方的方法。 226 | 虽然这在某些情况下可能看起来很有吸引力,但它对于安全性能有什么帮助并不十分明确,因为你只是转换了你信任的对象。 227 | 你不再信任你当前的互联网服务提供商,而是信任提供你虚拟私人网络服务的企业……你首先要相信他们是否已正确设置了这个虚拟私人网络业务,还要相信他们不会追踪你正在做什么。 228 | 这种信任的改变是否真的值得呢?如果你正在使用一些不安全的公共Wi-Fi网络,那么也许值得考虑。 229 | 但如果你在麻省理工学院,这并不明显。 230 | 你是否比信任麻省理工学院的IS&T更信任你的VPN提供商?或许你是这样认为的,但这是你需要考虑的决定,关于你信任什么、信任谁以及为什么?你还应该知道,你的很多流量,尤其是敏感性数据在互联网上,已经被加密了。 231 | 无论是HTTPS还是其他使用类似TLS的协议的数据,大部分敏感数据都已经通过加密通道发送。 232 | 如果你在一个不安全的Wi-Fi网络上,你的网络提供商是谁并不重要,重要的是你的流量是否加密。 233 | 那些重要的流量可能已经被加密了,也可能没有,如果没有,你的VPN提供商也能像托管这个不安全的Wi-Fi网络的人一样看到它的明文。 234 | 请注意,我在上面说的是“在最好的情况下”。 235 | 有些VPN提供商被证明是恶意的,会记录你的所有流量,将流量出售给第三方。 236 | 有些VPN提供商忘记在VPN上启用加密,所有这些都是真实存在的问题。 237 | 因此,您应该非常仔细地考虑VPN是否真正为您提供了任何好处。 238 | 有关VPN的问题?是的?(学生)所以我有一个关于公共Wi-Fi网络的问题,因为电脑和路由器之间的流量在计算机和路由器之间没有加密,对吧?除了通常通过HTTPS和[不可理解的]进行的内容。 239 | 那么这是否意味着人们可以通过DNS请求嗅探出我正在访问哪些域名?(乔恩)这是一个非常好的问题。 240 | 如果您在公共Wi-Fi网络上,则您与无线接入点之间的流量没有加密。 241 | 至少在外层上没有加密,但它可能在像HTTPS这样的加密层中加密。 242 | 243 | 这是完全正确的,观察Wi-Fi网络的人将能够看到未加密的任何内容。 244 | 但解决方法是加密所有流量,而不一定要通过VPN。 245 | 例如,可以使用DNS over TLS或DNS over HTTPS的方式来实现这一点,这可以使本来可能以明文泄露的信息得到加密。 246 | 而不是试图相信某个提供商为您做到这一点。 247 | 当然,在某些情况下,您可能有一个可信赖的机构为您提供VPN网络。 248 | 例如,MIT为所有MIT学生和工作人员提供VPN网络,您可以注册使用。 249 | 在这种情况下,您可能更信任MIT而不是其他可能连接的网络。 250 | 所以这可能值得一试,但这是您需要考虑的事情。 251 | (学生)当你说你可以用DNS加密它,是什么意思?(Jon)DNS是人们将域名转换成IP地址的方式,或者说是您的计算机将域名转换成IP地址以了解要连接到哪台计算机的协议。 252 | 默认情况下,该协议是明文的,没有任何加密。 253 | 有各种各样的方法可以加密DNS流量。 254 | 其中一些是标准化的,而一些不是。 255 | 我不会在这里详细介绍机制,但您应该搜索一下并查看一些方法。 256 | 好的,我想谈论的最后一件事是Markdown。 257 | 因此,很有可能你们中的一些人会在余生中写文本,并且你们会想以各种简单的方式标记这些文本。 258 | 你可以启动Word或使用LaTeX等来标记你的文档,但这是一种相当笨重的方法。 259 | 相反,如果我们能够以自然的方式写东西,那将是很好的。 260 | 我不知道如何更好地描述它,但是它是自然的方式,如果你想强调一个词,你只需要在它周围放上星号或其他东西,然后它就起作用了。 261 | Markdown本质上就是这样。 262 | 它是一种尝试将我们通常自然地编写文本的方式编码到一种标记语言中,使您可以编写粗体文本、链接、列表等等。 263 | 事实上,本课程的所有讲义都是使用Markdown编写的。 264 | Markdown非常简单明了,基本规则在讲义中,但您需要知道的基本事项是,在Markdown中,如果您在一个词的周围放上星号,那么这个词就被强调了。 265 | 或者是一些词序列。 266 | 如果您在一个词的周围放上双星号,那么这个词就会被强调为加粗。 267 | 您还可以做各种其他的事情,例如如果你在一行前加上破折号,那么这一行就成为一个列表了。 268 | 269 | 如果在一行前加上一个破折号,它就成为一个列表,然后你就可以添加列表项了。 270 | 如果在列表项前加上“1.”或其他数字,它就成为了一个有序列表。 271 | 如果在内容前面加上井号,它就会变成标题,就像是某种标题。 272 | 如果加多个井号,它们就变成了子标题,你可以继续添加更多。 273 | 如果你想编写代码,可以在单引号之间输入代码,然后它将以等宽字体呈现。 274 | 如果要多行代码,可以使用三个反引号,然后是代码和更多的代码,最后再使用三个反引号。 275 | 在许多情况下,如果你在GitHub上,你甚至可以在反引号之后输入一个语言名称,而不需要空格,它会以你选择的语言进行语法高亮显示。 276 | 这是一件非常方便的事情,在如此多的网站上都支持它,你甚至可能没有意识到。 277 | 就像在Facebook Messenger中,你可以使用许多这样的功能。 278 | 它们没有正式说明它们在任何地方都支持Markdown,但许多这样的事情只是发生了,值得学习至少基础知识并开始使用它们。 279 | 你也可以创建链接,但这已经在笔记中提到了。 280 | 有关Markdown的问题吗?好的,Anish,轮到你了。 281 | (Anish)我的麦克风工作吗?这个工作吗?你们能在后面听到我吗?灯亮了。 282 | 哦,我想我能听到。 283 | 好的,继续我们随机话题的主题,这些主题与我们之前讨论的主题都没有关系。 284 | 接下来我们要讨论的是一个叫做“Hammerspoon”的程序,它是Mac OS上进行桌面自动化的工具,我认为Windows和Linux也有类似的工具,很多想法可以继承下来。 285 | 如果你想知道如何在其他平台上实现这些功能,可以谷歌一下。 286 | 基本上,Hammerspoon是一个程序,它让你编写用Lua编写的脚本,这些脚本与各种操作系统功能进行交互。 287 | 因此,你可以编写代码来与键盘和鼠标交互,并将其连接到窗口管理、显示管理、文件系统、电池和电源管理、Wi-Fi等等。 288 | 基本上,所有操作系统管理的东西,这个工具都可以让你钩入这些东西。 289 | 因此,你可以通过编写几行代码做所有的很酷的事情。 290 | 这个工具可以做的一些很酷的事情的例子是:你可以绑定热键来移动窗口。 291 | 292 | 特定位置。 293 | 这里有一个演示。 294 | 我有这个窗口打开。 295 | 我按下,按照我的特定设置,"Option+Command+Right",这个窗口就会向右移动。 296 | "Option+Command+Left",这个窗口就会向左移动。 297 | 我还有一些其他的快捷键可以将窗口移动到不同的位置。 298 | 这样我就可以达到类似于Jon之前谈到的平铺窗口管理器的效果,将窗口移动到屏幕的不同部分,以特定的方式设置东西,而不必使用鼠标将东西放在我想要的位置,然后点击和拖动来调整窗口的大小。 299 | 只需要一个键盘快捷键就可以搞定。 300 | 但是这个工具不仅限于仅仅移动窗口并将其绑定到特定的键盘快捷键上。 301 | 你还可以做其他的事情,比如创建一个菜单栏按钮,里面有很多不同的选项,你可以将这些不同的选项绑定到不同的操作。 302 | 所以在我的特定情况下,我创建了这个小菜单,然后我有很多不同的事情,我相当频繁地做这些事情,点击这些事情会调用我编写的一个特定的Lua函数,与这个库进行交互。 303 | 例如,这个“Rescue windows”是一个我经常使用多个显示器的特定问题,有时我的操作系统会混淆,我有一些窗口出现在我的显示器之外,我该如何将这个东西拿回来呢?那就是这个“Rescue windows”所做的事情。 304 | 它将那些超出屏幕的窗口重新放回屏幕上。 305 | 我在这里设置的另一个很酷的东西是我有特定的布局,我已经命名了它们。 306 | 比如一个宿舍、一个实验室和一个笔记本电脑布局。 307 | 所以在我的实验室里,我有这个屏幕和另一个屏幕和另一个屏幕,方向不同。 308 | 我有这个特定的设置,我想在这里可能我的终端全屏显示,在这里显示我的聊天程序,这个屏幕分成五个不同的部分,有不同的程序放在不同的地方。 309 | 在我到实验室的时候,我可以点击“布局实验室”,它将调用一些代码,描述了一个特定的布局,这并不是很复杂,只有10行代码,它会实例化这个布局,并把所有东西放到需要放的地方。 310 | 我甚至可以在理论上自动化其中的一些过程,让我的电脑可以自己识别,比如我插上一个显示器,我的电脑就知道:“哦,这是你实验室里的显示器。 311 | 让我自动创建这个布局给你。 312 | 313 | 这是使用 Hammerspoon 工具的另一种方法。 314 | 还有其他一些有趣的事情,比如可以检测您所在的 Wi-Fi 网络,这样就可以知道您所在的位置。 315 | 也许我在家里和在实验室的 Wi-Fi 网络名称不同,那么我可以做一些事情,比如当我到实验室时自动将扬声器静音,这样我就不会在实验室里尴尬地播放音乐了。 316 | 另一个很酷的例子是,我有一台 Mac 电脑,它有一个高级电源适配器,我的很多朋友的电脑看起来都和我的一样,他们的电源适配器看起来也和我的一样,有时候我会用他们的电源适配器,因为我把我的忘在家里了或者其他原因。 317 | 这个工具实际上可以使用三到四行代码做一些好玩的事情,比如在您意外拿了朋友的电源适配器并插到电脑上时,会弹出一个警告。 318 | 所以,从高层次上讲,这个工具允许您运行任意 Lua 代码,并将其绑定到菜单按钮或按键上,它与操作系统的大部分交互,以执行各种有趣的事情。 319 | 这就是 Hammerspoon,对此还有什么问题吗?好的,接下来讲另一个话题,和之前没有任何关系,就是启动和使用 Live USB。 320 | 您的计算机上的操作系统,比如 Windows 或 Mac OS 或其他您习惯的操作系统,实际上不是在开机时第一件运行的东西。 321 | 在操作系统加载之前,启动过程中会发生其他一些事情。 322 | 有些有趣的东西可以在这里配置。 323 | 您可能已经看到,当您打开计算机时,它会显示类似“按 F9 键配置 BIOS”或“按 F12 进入启动菜单”的消息。 324 | 特定的按键序列可能会因您的机器和具体配置而有所不同,但这是一个通用的模式,您可以在这里配置各种有趣的硬件相关的内容。 325 | 因此,这值得一试。 326 | 在这个启动菜单中,您可以让计算机从备用启动设备开始启动。 327 | 例如,默认情况下,我的笔记本电脑有一个固态硬盘,当它开机时会启动 Mac OS,但是我也可以插入一个安装有操作系统的 USB 闪存驱动器,然后在开机时告诉计算机从那个闪存驱动器而不是内置的固态硬盘开始启动。 328 | 这对于例如我破坏了操作系统安装,需要做一些事情,比如获取计算机上的数据,或者修复操作系统。 329 | 比如可能有些关键文件被删除了,或者我忘记了密码,需要去调整一些文件以重置它。 330 | 从一个Live USB启动计算机,从安装在闪存驱动器上的独立操作系统引导,可以让我做到这一点,就像启动我的操作系统,挂载在我当前机器上的硬盘一样。 331 | 332 | 我正在处理的任务,然后进行一些调整或者将数据复制出来。 333 | 因此,live USB非常有用,在课堂笔记中,我们提供了一个工具,可以帮助您轻松创建live USB。 334 | 有关启动过程或live USB的任何问题吗?好的,下一个话题是虚拟机、Vagrant、Docker、云和OpenStack。 335 | 去年我们整整开了一个讲座来探讨这个话题,今年我们将把它压缩到一分钟内。 336 | 在高层次上,虚拟机和类似的工具,如容器,可以让您在当前计算机系统内模拟整个计算机系统,例如,我正在运行Mac OS,但在我的Mac OS环境内,我可以模拟运行Ubuntu或其他操作系统的机器。 337 | 这是创建用于测试、开发或探索的隔离环境的好方法,例如运行潜在的恶意代码应该与我的当前环境隔离。 338 | 我认为程序员最常见的用例是使用虚拟机或容器创建开发环境。 339 | 我使用Mac OS,并在我的当前计算机上安装了一些服务、库等,但我可能想要在Debian机器上运行某些Web编程项目,并需要安装Postgres等数据库服务器,而不是在我的Mac OS机器上安装所有这些。 340 | 我可以为开发目的实例化这个新机器。 341 | 虚拟机是一个普遍的概念。 342 | 有许多程序可以被称为虚拟机超级监视程序,支持您在计算机上实现这个功能。 343 | 还有一些工具,让您可以脚本化这些超级监视程序,以便指定机器配置,如操作系统,您想要安装哪些软件包以及您想要安装哪些服务。 344 | 这是屏幕上的一个示例。 345 | 这是使用名为Vagrant的系统完成的。 346 | 在课程笔记中有链接,如果您感兴趣可以了解一下。 347 | 因此,在简短的纯文本文件中,我可以指定一个正在运行Debian的机器,它应该安装了Postgres、Redis和Python等软件。 348 | 然后,一旦我有了这个配置, 349 | 我只需要输入"vagrant up",它会读取这个文件并根据这个配置实例化一个新的虚拟机。 350 | 然后,在我这样做后,我可以通过"vagrant ssh"来SSH进入这个虚拟机。 351 | 所以它不是在其他硬件上运行的远程机器,它只是在我的机器上模拟,但现在我在这里有一个像我现在拥有的Ubuntu盒子一样的盒子,让我们真正地惊奇一下,这里没有一堆的debian和我想要安装的所有东西,我可以在这个隔离的环境中进行开发,而不是在我的MacOS机器上安装所有这些垃圾。 352 | 现在有类似的工具,如Docker,它们在概念上类似,但是使用容器而不是虚拟机。 353 | 这是一个区别,我们现在不会详细讨论。 354 | 因此,你可以在自己的计算机上运行虚拟机,但你也可以在云上租用虚拟机,这是一个很好的获得即时访问的方式,比如你可能想要一个始终开着、始终连接到互联网并具有公共IP地址的计算机。 355 | 比如你想运行一个始终可用的Web服务器,或者你想运行一些其他的服务,比如一个slack机器人之类的东西。 356 | 在云上租用虚拟机是一个不错的选择,这些虚拟机对于低容量的机器和小型CPU和少量的磁盘空间来说都是很便宜的。 357 | 你可能想做的是获得一个非常强大的机器,比如有很多CPU核心或者有很多内存或者有大量的GPU,用于某些特定的目的,比如说你正在进行深度学习或者其他一些敏感的计算。 358 | 那么,这也是使用云上的虚拟机可以做的事情。 359 | 最后,你可以获得比你实际可以访问的机器更多的机器。 360 | 比如,如果我需要一千台机器,但只需要两分钟来完成一些非常并行的任务,那么我可以很容易地使用虚拟机来完成。 361 | 用于执行此操作的流行服务包括Amazon AWS或Google Cloud。 362 | 如果你是MIT CSAIL的成员,你也可以使用CSAIL OpenStack获得免费的虚拟机进行研究。 363 | 所以,关于虚拟机、Vagrant、Docker或其他类似的东西,有任何问题吗?问题是当我说我在运行Ubuntu时,实际上在这种情况下,我运行的是Debian,那么我是否像运行Ubuntu一样在我的电脑上安装了Ubuntu,还是发生了什么情况?基本上,当我键入“vagrant up”时,Vagrant为我做的是,因为我指定我想要Debian,它从互联网上下载了Debian,为这台新机器设置了一个磁盘映像,将Debian安装到该磁盘映像中,然后安装了这些程序。 364 | 所以,是的,这在我的电脑上。 365 | 366 | 但是,所有这些都只是在一个特定的文件中,这是一个磁盘映像。 367 | 然后,我正在模拟一台与我的电脑完全隔离的机器。 368 | 这是在我的电脑上作为一个进程运行的。 369 | 这回答了你的问题吗?还有其他关于虚拟机的问题吗?好的,下一个话题也会是一个简短的提及。 370 | 很多人是程序员,你们习惯使用像Vim这样的工具编写程序,或者使用其他你们熟悉的编辑器。 371 | 但是,另一件非常实用的东西是,可以为特定任务使用笔记本编程环境。 372 | 这是一种更加交互式的编程方式。 373 | 在屏幕上,我有一个演示。 374 | 这是一个叫做Jupyter笔记本,它可以用于编写Python程序。 375 | 我认为它们也支持其他一些语言。 376 | 基本上,这是一个很好的交互式编程方式。 377 | 通常,你们习惯在一个文件或一组文件中编写一个大程序,一旦完成,就可以运行整个程序。 378 | 但是,这让你更加灵活,并逐段运行代码。 379 | 例如,我可以将我的程序分成这些小段,这只是我写的一些随机代码。 380 | 然后,我可以说,“执行这个单元格”,然后按下特定的组合键来执行该单元格。 381 | 但是,我可以回去稍微修改我的程序,例如,我想要将它变成小写。 382 | 然后,我可以执行这个单元格,然后去评估这个东西,这样我就可以在Python环境中运行一些小段的代码。 383 | 这是一个很好的逐步构建程序的方式,而不是一次性编写所有代码。 384 | 这对特定的研究目的非常有用,例如,我认为很多人用它来进行机器学习工作。 385 | 对于笔记本编程环境的概念有任何问题吗?值得一看。 386 | 哦,问题是,“这看起来像是在线的,有没有Jupyter笔记本的离线版本?”实际上,这是在浏览器中运行的东西,但它是本地运行的……所以,我不知道你能否在屏幕上看到,因为它有点小,但在这里,我在自己的本地机器上运行一个Jupyter笔记本。 387 | 他们只是建立了它,让它在Web浏览器中运行。 388 | 话虽如此,也有在线Jupyter笔记本可供使用,其中Python内核实际上在一些远程机器上运行。 389 | 你可能会想这样做,例如,在我的笔记本电脑上,我没有一个高级的GPU, 390 | 在我的房间里,我有一台装有高级GPU的机器。 391 | 所以,当我做机器学习的工作时,我经常通过SSH登录到那台机器,运行Jupiter笔记本,然后在本地的Web浏览器中打开界面,以便访问那台运行在另一台机器上的强大GPU。 392 | 还有其他问题吗?好的。 393 | 今天我们要讨论的最后一件事是Github。 394 | 我们在版本控制讲座中已经提到了一点。 395 | 但是,Github是最受欢迎的开源软件开发平台之一。 396 | 它托管源代码和git存储库。 397 | 但是,他们还有其他工具来管理项目。 398 | 而且,像我们在这门课程中谈论过的许多工具一样,都托管在Github上。 399 | 例如,像我们刚刚谈论的Hammerspoon就是在Github上开发的。 400 | 在Github上开始为开源项目做贡献以帮助改进您每天使用的工具非常容易。 401 | 您可以通过两种主要方式为Github上的项目做出贡献。 402 | 让我们打开一些存储库。 403 | 我们实际上可以进入课程网站的Github存储库。 404 | 这是一个开源软件项目。 405 | 让我们放大一点。 406 | 因此,您可以通过问题和拉取请求这两种主要方式为Github上的项目做出贡献。 407 | 实际上,对于开发人员来说,一件非常有用的事情,对用户来说也相对轻松和简单,就是报告软件项目的问题。 408 | 比如说,您正在使用某个程序,遇到了一些错误...编写高质量的问题对开发人员非常有帮助,希望不会花费太多时间。 409 | 因此,您可以前往Github上找到项目,进入问题页面,然后单击新问题,并编写一些高质量的错误报告。 410 | 然后,希望开发人员会回复并为您修复问题。 411 | 例如,在这门课程中,一个学生指出了我们的讲座笔记中的问题,我说,好的,看起来是一个合理的问题。 412 | 让我们来解决它。 413 | 在这种情况下,我实际上询问了这个人,“他们只是想为我解决问题吗?” 414 | 因此,我想谈论另一件事:问题和拉取请求。 415 | 拉取请求是在Github上为项目做出贡献的第二种方法。 416 | 这涉及实际向项目贡献代码。 417 | 如果我们查看此项目的拉取请求,您会看到许多人提交了代码更改。 418 | 创建拉取请求的过程比提交问题要更复杂一些。 419 | 您不只是提交文本:您实际上要修改他们的源代码。 420 | 我们提供了一些解释该过程的指南链接。 421 | 但总体来说,您需要在Github上“分叉”存储库,然后将其下载到本地,这样就有了自己的本地副本。 422 | 然后,您可以开始工作,进行一些开发工作,修复错误或添加功能,最终将所做的更改发送回原始开发人员,称为“拉取请求”。 423 | 然后,通常情况下,项目维护者将与您反复回复,给您反馈您所提出的更改。 424 | 最终,一旦大家都满意,他们会合并您的更改,这些更改将对使用该项目的所有人都可用。 425 | 这就是如何在Github上为项目做出贡献,使软件对每个人都更好。 426 | 对于Github有任何问题吗?明白了,好的。 427 | 所以,今天的话题就到这里。 428 | 对于整个讲座有任何问题吗?很好,那么在结束之前,让我简单介绍一下明天的讲座:今天是我们认为有趣的所有主题,我们应该讨论的所有主题。 429 | 明天的讲座将是一个问答环节。 430 | 在今天的讲座之后,我们将通过电子邮件发送一个链接,您可以通过该链接提交问题供我们回答。 431 | 请务必填写,否则,我们明天将没有太多可谈的内容。 432 | 好的,希望明天在我们的问答讲座中见到您。 433 | -------------------------------------------------------------------------------- /01_ai_sub/ch_subtitles/lecture06_ch_ai_sub.md: -------------------------------------------------------------------------------- 1 | 好的,让我们开始今天的讲座吧。 2 | 在开始之前,我有一个关于办公时间的小提示。 3 | 根据调查,有些人误认为每次讲座后的办公时间只是针对当天的讲座主题,但实际上不是这样的。 4 | 你可以在办公时间来问我们任何问题,无论是关于前一天还是前一周的讲座,甚至是这门课程没有完全涵盖但你感到好奇的内容。 5 | 因此,如果你有关于任何事情的问题,都可以来办公时间问我们。 6 | 办公时间在32 g9休息室,这是32号楼,也称为Stata中心,有两座塔楼,G塔和D塔。 7 | 我们在Gates塔的第九层,所以如果你乘电梯一直上去,休息室就在你面前。 8 | 好的,很棒。 9 | 今天,我们将谈论版本控制系统。 10 | 我想了解一下你们是否有使用过版本控制系统。 11 | 所以,如果你有使用过git或任何其他版本控制系统,如subversion或mercurial等,能否举手呢?哦,太好了,有很多人。 12 | 因此,我就不会过多地谈论版本控制系统了,而是会很快地进入git及其数据模型和内部细节的详细讨论。 13 | 但是,简单地概括一下,版本控制系统是用于跟踪源代码或其他文件或文件夹集合的更改的工具。 14 | 正如名称所示,这些工具帮助跟踪某些文档的更改历史。 15 | 除了这些,它们还有助于协作,因此它们在软件项目团队中非常有用。 16 | 版本控制系统通过一系列快照跟踪文件夹及其内容的更改。 17 | 因此,你可以捕捉文件夹和其中的所有内容(例如软件项目),并在一系列快照中拥有多个版本。 18 | 每个快照都封装了某个顶级目录中包含的所有文件和文件夹的完整状态。 19 | 版本控制系统还会保存一些元数据,以及实际内容的更改。 20 | 这是为了方便确定某个人对特定文件的更改,或者何时进行了特定的更改。 21 | 因此,版本控制系统维护作者和提交时间戳等元数据,你也可以将额外的消息附加到这些快照上等。 22 | 那么,为什么版本控制很有用呢?即使你在独自完成项目,也很有用。 23 | 因此,您可以使用它查看您编写的旧版本代码,通过查看提交信息找出为什么更改了某些内容,使用不同的开发分支并行处理不同的事情而不会发生冲突,或者能够在保持不同功能独立的情况下修复错误,等等。 24 | 因此,即使您只是在小规模项目上工作,它也是一种非常有用的工具。 25 | 就像我认为这门课的讲师们即使在作业或班级项目等小规模事物上也使用git一样。 26 | 27 | 除了我们的研究或较大的软件项目外,版本控制还是一个非常强大的与他人合作的工具。 28 | 因此,它对于传递代码补丁,当不同人同时在同一代码块上工作时解决冲突等方面非常有用。 29 | 因此,无论是独自工作还是与他人合作,它都是一个非常强大的工具。 30 | 此外,它还具有一些非常实用的功能,可以帮助回答某些比较困难的问题,例如软件项目中编写了特定模块的人是谁,或者编辑了特定行的人是谁,为什么更改了这个特定的行,它是什么时候更改的,由谁更改的等等。 31 | 版本控制系统还具有一些非常强大的功能,我们可能会在今天的讲座结束时介绍,或者您可以在讲座笔记中找到。 32 | 例如,假设您已经在某个项目上工作了几年,然后注意到项目有些奇怪的地方已经损坏了,比如您有一些单元测试不再通过,而这不是刚刚才坏了,而是在一段时间前损坏了,您不知道这个回归是什么时候引入的。 33 | 精心编写的控制系统有一种自动识别这种情况的方式,比如您可以将其拿出来并给它一个单元测试,该测试目前失败,但您知道在过去的某个时候它是通过的,它可以二分搜索您的历史记录并确定到底是哪个代码更改导致了它的失败。 34 | 因此,如果您知道如何正确使用这些工具,就会有很多非常强大和高级的功能。 35 | 市面上有许多版本控制系统,Git已成为版本控制的事实标准,因此我们将在今天的讲座中介绍它。 36 | 我想向您展示一张漫画,之前已经放在屏幕上了,让我把它拿回来。 37 | 这是一部xkcd漫画,说明了Git的声誉。 38 | 我念给你们听。 39 | 'Git通过美丽的分布式图论树模型尝试协同工作。 40 | 很酷。 41 | 我们如何使用它?没想到。 42 | 只需记住这些shell命令并输入它们以同步。 43 | 如果出现错误,请将您的工作保存在其他地方,删除项目并下载新的副本。 44 | '也许有些人可能不想举手,但如果你以前做过这件事,请举手。 45 | 我学习这个工具的时候也曾经这样做过,我想你们中的许多人也尝试过。 46 | 所以,本讲座的目标是让你们不必再这样做。 47 | 不幸的是,正如这个漫画所描绘的那样,Git的界面设计相当糟糕。 48 | 它是一个泄漏的抽象层,因此出于这个原因,我们认为自上而下地学习Git,从界面开始,可能不是最好的方式,它会导致一些混乱。 49 | 就像这个漫画所示,你可以记住几个命令,并将它们视为魔法咒语,认为一切都正常工作。 50 | 这样做还算可以,但是一旦出了问题,你就必须像这个漫画中描述的那样处理。 51 | 虽然Git有一个丑陋的界面,但它的基本设计和思想实际上非常优美。 52 | 53 | 一个丑陋的界面需要被记忆,但是Git下面美妙的理念却可以被真正理解。 54 | 一旦你理解了Git的内部结构和数据模型(实际上并不那么复杂),你就能学会Git的界面。 55 | 你需要记住一些东西,但是通过理解它们是如何操作底层数据模型的,你可以理解某些命令的确切作用。 56 | 所以,我们今天教授Git的方式是,首先抽象地讲述数据模型,讨论如何对文件和文件夹进行建模、历史快照以及它们之间的关系。 57 | 然后,我们将介绍一些Git命令,最后,在资源和练习中,我们将链接到教程,让你学会所有具体的命令,因为你最终需要学习很多不同的命令。 58 | 关于我们今天的教学方法,你们有什么问题吗?好的,那我们开始吧。 59 | 可能有很多临时方法可以用于版本控制,我猜你们中的一些人可能以前就这样做过。 60 | 比如说,你有一些文件或文件夹,对应于一个系统软件项目,你想追踪变化。 61 | 你可以每天复制整个文件夹,并给它加上一个时间戳。 62 | 当你想与其他人合作时,你可以将整个文件夹转换成一个zip压缩文件,并将其发送给某个人。 63 | 然后,当你和你的朋友在软件项目的两个不同特性上工作时,你们可以并行地工作。 64 | 然后,其中一个人将zip文件发送给另一个人,然后你手动复制并粘贴适当的代码片段到你的代码中,以便最终得到一个具有你们两个特性的代码。 65 | 这种方法可能会有所作用。 66 | 如果你曾经这样做过,请举手。 67 | 我肯定做过。 68 | 尽管如此,还有一些人认为我们不应该这样做。 69 | Git有一个经过深思熟虑的模型,可以促进这些交互,例如跟踪你项目的历史记录、协作等等。 70 | Git将历史记录建模为一组文件和文件夹,位于某个顶层目录中。 71 | 因此,你可能已经从自己计算机上的文件和文件夹的抽象概念中熟悉这种建模方法。 72 | 这里是一个例子:你可能有一个顶层目录,我会称之为根目录,这个目录中可能有一个名为foo的文件夹,这个文件夹中可能有一个名为bar.txt的文件,其中可能有一些内容,比如“Hello world”。 73 | 74 | 然后,也许这个最高层目录中有一个文件夹,它也可以有另一个文件。 75 | 比如说,还有一些其他的文件,这个文件也有一些内容。 76 | 很简单。 77 | Git用于这些不同内容的术语是这样的:最高级的东西称为树。 78 | 所以这是一个文件夹,然后我们通常称为文件的东西称为blob。 79 | 好的,现在我们有了一个文件和文件夹的模型,这是一种递归数据结构。 80 | 树可以包含其他树,然后树可以包含树和文件。 81 | 显然,文件不能包含树。 82 | 好的,现在我们有了一个文件和文件夹的模型,而这个东西的顶级,我刚刚标记的“根”,是正在被跟踪的目录。 83 | 就像您可能在计算机上有一些文件夹对应于软件项目一样。 84 | 现在,如何建立历史模型呢?一旦你有了文件和文件夹的模型,你可以想象一种方法,就是你对整个东西进行快照,然后历史就是一系列的快照。 85 | 就像你可以想象它,你几乎可以认为你有日期和时间戳的文件夹副本。 86 | 嗯,它不使用这样简单的线性模型。 87 | 它使用的是一种稍微复杂的东西。 88 | 你可能听过这个术语,但是Git使用有向无环图来模拟历史。 89 | 这听起来像一堆花哨的数学词,但实际上并不是很复杂。 90 | 在Git中,每个快照都有一些父节点,基本上,我们想知道哪个变化先于另一个变化。 91 | 所以假设这里,我将使用圆圈来表示单独的快照。 92 | 这是这个树中的所有内容,所以是我的项目中的所有文件和文件夹。 93 | 我的整个项目可能处于某种状态,然后我编辑了一些文件,现在它处于某种其他状态。 94 | 然后我添加了一些文件,它又处于另一种状态。 95 | 每个状态都指向先前的状态。 96 | 到目前为止,这是一个线性历史,但它让我们能够做一些稍微花哨的事情。 97 | 你还可以从某个快照分叉你的历史,并说,“我想基于这个版本做出一些更改,并创建一个新的快照。 98 | ”这种建立历史的方式允许您做一些像“好的,我正在开发我的项目。 99 | 这是我的主要开发线路。 100 | 我到这里了,现在我有两个不同的任务要完成。” 101 | 假设一方面,我有一些想要添加到我的项目中的新特性,所以我要花几天的时间去工作。 102 | 但另一方面,有人向我报告了一个 bug,我需要去找出这个 bug 并修复它。 103 | 那么,与其同时在同一条开发线上并发地工作在所有这些事情上,Git 有其将历史记录分支成两个独立的分支的方式,并以一种不相关的方式同时处理不同的事情。 104 | 因此,我可以使用这个基本快照,例如我的项目处于一种可工作的状态,然后从这里开始实现一个新的特性来创建一个新的快照。 105 | 因此,这个快照包括基本项目和一个新的特性。 106 | 同样地,与此同时,我可以回到这个原始快照,因为我不想在实现新特性时进行 bug 修复,所以我可以回到这里,然后进行 bug 修复并创建一个不同的快照。 107 | 因此,这个快照只有 bug 修复,而没有特性。 108 | 最后,一旦我并行地完成了这两件不同的事情,最终,我想将它们全部合并到我的共同源代码中,既包括特性,也包括 bug 修复。 109 | 因此,最终,我可以通过合并这两个不同快照中存在的更改来创建一个新的快照。 110 | 因此,这个新的快照将具有这两个快照作为父节点,这个版本将同时包括特性和 bug 修复。 111 | 所以,是否理解了 Git 以一种比文件和文件夹的序列更为复杂的方式来模拟历史记录的原因?为什么要支持分支以并行工作,然后再合并来自不同并行开发分支的更改呢?问题:是的,这是一个很好的观点。 112 | 似乎当您合并时,可能会创建意外的错误。 113 | 您可以想象这里的特性实际上更改了某些使这个 bug 修复变得无关紧要的东西,或者您可以想象这个 bug 修复破坏了这个特性或其他类似的情况。 114 | 答案:哦,这是一个非常好的观点。 115 | 这就是所谓的合并冲突,Git 在合并并行开发分支时会尝试自动以保留所有重要更改的方式来合并这些更改。 116 | 但如果它变得混乱了,它会报告一个合并冲突,然后让程序员自己决定如何将并发的更改组合到同一文件或其他类似的地方。 117 | 然后Git有一些工具来帮助解决这个问题。 118 | 还有其他问题吗?太好了。 119 | 现在我们有了文件和文件夹的模型,还有一个关于历史的模型,它描述了我们代码的不同快照之间的关系。 120 | 这里有一个小细节,每个圆圈都对应一个快照,就像一个带有文件和文件夹的树一样,但它们还有一些元数据。 121 | 例如,在这里,我们可能会看到提交的作者是我,还有其他元数据,例如与此提交相关的消息。 122 | 我可以描述我所做的更改类型,这些更改在该快照中存在但在上一个快照中不存在。 123 | 这不是椅子类,所以下一步我们要谈论比这更低一层的内容,即Git内部如何表示这个数据结构。 124 | 因此,我将写下伪代码,因为我认为这是最容易理解的方式。 125 | 126 | 首先我们有文件。 127 | 所以一个blob只是一堆字节,我会说这是一个字节数组。 128 | 好的,那么什么是tree?记住这只是一个文件夹,那么文件夹是什么?它们是从文件名或目录名到实际内容的映射,而内容可以是另一个tree,如子树,或者是文件。 129 | 最后,我们有最后一件事情,到目前为止我一直称之为快照,在Git术语中它们被称为提交。 130 | 那么提交做了什么?它有一堆东西。 131 | 提交有描述它们之前的父项,因此在大多数普通提交的情况下,它们有一个父项,就像它们来自哪里一样。 132 | 合并提交可以有多个父项,因此父项是提交的数组,然后我有一些元数据,如作者和可能的消息,最后是实际内容,快照,它是一个树,是对应于特定提交的顶级树。 133 | 这是一个非常干净简单的历史模型,这基本上就是Git模型历史的全部内容。 134 | 关于这个有问题吗?好的,现在我们深入一些。 135 | 让我们谈谈它实际上是如何存储和寻址这些实际数据的。 136 | 在某个时候,这实际上必须转换为磁盘上的数据,对吧?因此,Git定义了一个对象,这是一个大型的术语,但是对象可以是其中三个中的任何一个,因此它可以是blob、tree或commit。 137 | 然后在Git中,所有对象都是内容寻址的。 138 | 所以Git在磁盘上维护的是一组对象,这些对象被维护为这个内容地址存储。 139 | 因此,如果你有其中的任何一个对象,你将它放入这个存储的方式是它的键是对象的哈希值。 140 | 例如,在伪代码中,我可能会说为了存储特定的对象o,我计算它的ID,通过对o进行SHA-1哈希,然后将其放入我的对象映射中,存储到磁盘上。 141 | 一个快速举手的人,谁知道哈希函数是什么?好的,我会快速总结一下。 142 | 基本上,哈希函数是你可以把它看作是这个神奇的函数,它将大量数据转换为一个短字符串。 143 | 在高层次上,这些被用来,或者说这可能是足够的临床医生,我不会在这里深入讨论太多细节,但是如果你感兴趣,可以在之后问我。 144 | 基本上,哈希函数给了我们一种基于所输入的内容,从而以一种确定性的方式为其命名的方法,并为其提供一个简短的名称。 145 | 然后,与存储相反,我们可以通过ID来查找和加载存储在存储库中的内容。 146 | 这就是我们通过ID从对象存储库中检索内容的方法。 147 | 有关此方面的任何问题吗?好问题。 148 | 那么它用的是哪种语言编写的?它是用我刚刚编写的伪代码编写的。 149 | Git的实现主要是用C编写的,并包括一些Bash和Perl脚本,我想。 150 | 151 | 还有其他问题吗?这个虚构的语言是否足够清晰,还需要解释其中的某些方面吗?好的,Blobs、树和提交在Git中是以这种方式统一的。 152 | 它们都是对象。 153 | 而且,正如您在这里描述的那样,看起来提交包含了许多其他提交和快照等内容。 154 | 实际上并不是这样。 155 | 相反,所有这些都是指针。 156 | 因此,提交将能够通过它们的ID引用许多父对象。 157 | 所以这实际上不是提交本身的数组,而是ID。 158 | 同样,提交中的快照不是实际的树对象,而是树的ID。 159 | 因此,所有这些对象都存储在这个对象存储中,并且所有对不同对象的引用仅通过它们的ID,即它们的SHA-1哈希来实现。 160 | 这有意义吗?你几乎可以将其映射到像Java这样的编程语言中的对象,这是对树的引用。 161 | 因此,它就像一个指针,而这就是您的领域。 162 | 也许这对您有所帮助,也许没有。 163 | 是的,确切地说就是这样。 164 | 那么,这是Git的磁盘数据存储。 165 | 这是一个内容地址存储,其中对象通过其哈希寻址。 166 | 对此有任何问题吗?好的,现在我们有一种识别的方法。 167 | 我们将所有不同类型的对象统一为称为对象的一种类型,并通过它们的SHA-1哈希方式来标识对象。 168 | 这些实际的SHA-1哈希长什么样子?它们是40个字符长的十六进制字符串。 169 | 就像SHA-1是一个160位的哈希函数,因此由该SHA-1函数返回的实际ID之一将是一个非常长的字符串。 170 | 因此,我们将有识别这些不同事物的方法。 171 | 就像对于一个3-2 CEB,我们会有相应的ID,例如某个东西。 172 | 173 | 现在我们有一种命名提交图中所有内容的方法,但这些名称非常不方便,因为它们非常长,而且像文本字符串一样,对人类来说没有任何意义。 174 | Git解决这个问题的方法是另外一件事情。 175 | Git维护一组对象,然后维护一组引用。 176 | 引用是什么?我来擦掉左边的一部分。 177 | 引用位于这里。 178 | 所以这是Git内部维护的另一个数据部分。 179 | 引用是一个从字符串到字符串的映射,你可以将其看作是将人类可读的名称映射到那个长的十六进制字符串。 180 | 因此,使用这些引用,你可以想象如何创建新的引用和更新引用等。 181 | 有了这个,我现在可以用名称来引用我的提交图中的内容,因此我可能会将其命名为“修复错误”或者我可能会为此处的内容命名等等。 182 | 因此,是的,使用这个,Git可以使用人类可读的名称来引用历史中特定的快照,而不是使用这些长长的十六进制字符串。 183 | 这里还有一件需要注意的事情是,鉴于Git的历史设计,整个图形实际上是不可变的。 184 | 你可以添加新的内容,但你实际上无法操作这里的任何东西。 185 | 我不会详细解释为什么或如何,但是请假设这是事实。 186 | 然而,引用是不可变的。 187 | 因此,在更新历史记录时,假设你继续在这个软件上工作,你创建了一个新的提交,我用圆圈表示它。 188 | 这个提交指向前一个提交。 189 | 我实际上可以将我的“修复错误”引用指向这里。 190 | 我可以更新此引用以现在指向这里。 191 | 但是,例如,我不能使其指向这里。 192 | 那甚至都没有意义,因为这只是这个对象的哈希值。 193 | 要更改此哈希值,我需要更改对象的内容,这并不真实。 194 | 好的,有关此方面的任何问题吗?这基本上就是Git的数据模型。 195 | 196 | 然后我们将进入通过命令行与 Git 进行交互,并看到 Git 命令如何与图形数据结构的操作相对应。 197 | 如果关于将历史记录建模为树、子树和快照、这些称为提交的东西如何链接在一起以及如何使用引用指向该图中的特定节点等方面有任何问题,请提出来。 198 | 好的,没有问题了?所以基本上,一旦我们拥有了对象和引用,那就基本上是 Git 存储库的全部内容。 199 | 这些是它存储的两个数据部分,在高层次上,所有 Git 命令行命令只是对引用数据或对象数据的操作。 200 | 所以,在本讲座的剩余部分中,我将介绍一些 Git 命令。 201 | 这基本上将是一个交互式演示,类似于 Vim 讲座,然后您可以参考笔记以获取有关这些命令的完整信息。 202 | 当然,它是一个非常强大的工具,我们无法在20分钟内覆盖所有内容。 203 | 好的,那么我要转到一个名为playground的文件夹,并创建一个名为demo的新目录。 204 | 进入demo,该目录将表示我的项目的顶层。 205 | 它当前是空的,因为我刚刚创建它。 206 | 如果我想将其转换为 Git 存储库,则使用 'git init' 命令。 207 | 'Git init' 代表 Git 初始化,我们看到它说 'initialized empty Git repository in blah blah slash dot Git'。 208 | 如果我输入 'ls',我仍然什么也看不到,但是如果我输入 'ls -a',这个目录中有一个名为 '.git' 的隐藏文件。 209 | 如果我输入 'ls .git',则会有一些东西显示在这里。 210 | 这是磁盘上 Git 存储其所有内部数据的目录,即对象和引用,实际上在这里,对象和引用是两个目录,所有存储库数据将存储在这两个目录的下面。 211 | 在我们进行这些操作时需要记住一个字母命令,那就是称为 'git help' 的命令。 212 | 'Git help' 将子命令作为参数,它会为您提供有关该命令的帮助。 213 | 例如,如果我输入 'git help init',它将告诉我有关 'git init' 命令的信息。 214 | 现在有一些命令可以用于查明 Git 存储库的情况,例如 'git status'。 215 | 在高层次上,它说目前的情况是什么,我们在这里看到(现在先忽略第一行),第二行说 '还没有提交'。 216 | 这是因为我们刚刚初始化了一个全新的存储库,所以还没有历史记录。 217 | 我现在要......还有人需要吗?清晰地擦掉这个黑板的这部分。 218 | 随着我们进行Git命令的输入,我会画出底层的对象和引用数据如何变化的图示。 219 | 因此,现在这张或者说没有图示代表着我们仓库的当前状态。 220 | 221 | 它是空的。 222 | 没有快照。 223 | 那么让我们来修复一下。 224 | 让我们添加一些内容到我们的历史记录中。 225 | 在这里我们没有文件,所以让我来创建一个文件'hello.txt',内容为'Hello, world!'。 226 | 通常情况下,您会在其中放置实际有用的源代码。 227 | 现在我想要做的是,当我输入某些Git命令时,随着我们的进行,绘制底层对象和引用数据发生变化的方式。 228 | 因此,现在,这张图片或缺乏图片代表我们仓库的当前状态。 229 | 您可以想象一个用于此的界面,其中有一个git快照命令或获取其他命令,它将获取当前目录的整个状态的快照。 230 | 由于许多原因,Git没有一个完全像那样工作的命令,因为Git希望为您提供一些灵活性,以便包括在您下一个快照中的更改。 231 | 这有时对初学者来说有些困惑,因此我现在尝试解释一下。 232 | 233 | 234 | Git有一个称为staging area的东西,从高层次上讲,这是您告诉Git应包括在您下一个快照中的更改的地方。 235 | 如果我们在这里执行git status,我们会看到Git说“尚未提交任何更改”,就像之前一样,并且它说“未跟踪文件hello.txt”。 236 | 这意味着Git注意到当前目录中有一个新文件,但是它不会被包括在下一个快照中。 237 | Git现在有点忽略它。 238 | 但是,如果我执行git add hello.txt并再次执行git status,它会说“现在提交的更改:新文件hello.txt”,因此,如果我执行git快照命令,实际上是git commit,它会创建一个我在那边画的那些圆圈中的新圆圈,并将该文件包含在我即将要进行的快照中。 239 | 所以让我继续运行git commit。 240 | 它弹出我的文本编辑器,让我输入与此提交相关联的消息。 241 | 编写高质量的提交消息真的很重要,因为以后当您查看项目的版本历史记录时,您会知道为什么进行了某些更改。 242 | 我将添加这个相对无用的提交消息,但是我们在讲义中有一个指南,告诉您如何编写高质量的提交消息。 243 | 现在我已经这样做了,Git输出一些输出。 244 | 主分支,暂时忽略那部分。 245 | 这个东西是我刚刚创建的提交的哈希值。 246 | 因此,现在我在我的历史记录中有一个单一节点。 247 | 这个节点里有一个树形结构,其中包含一个单独的 blob,也就是一个文件 hello.txt,内容是“hello world”。 248 | 然后这个节点有一个 SHA-1 哈希值,是2fb等等一些值(实际上在 Git 界面中也被截断了)。 249 | 250 | 这只是再次输出我的提交消息,并提醒我刚刚添加了hello.txt文件。 251 | 如果我现在使用git log命令,它非常有用,因为它可以帮助你可视化历史记录、提交图形,如果我做... 这是一个很好的问题。 252 | 那么这个哈希值到底对应什么?这是提交的哈希值。 253 | 提交内部包含树的哈希值,以及其他任何信息。 254 | 所以我可以使用git cat-file -p这个号码。 255 | 这有点像一个Git内部命令,可以打印出这个提交的内容,因此您可以看到它映射到我在那边画的数据结构。 256 | 所以这个提交里面有这个树,然后我是作者,这是提交的消息等等,我可以继续往下挖。 257 | 所以,您可以取这个树的哈希值,并执行git cat-file -p这里的哈希值。 258 | 它说这个树里面有一个名为hello text的条目,那个文件有一个blob,并且有这个哈希值。 259 | 我可以做git cat-file -p <哈希值>,它会显示我那个文件的实际内容。 260 | 这些都是探索对象存储中对象的内部Git命令。 261 | 问题是一个好问题。 262 | 那么问题是,为什么我必须使用git add?为什么不能只提交所有更改?答案是,其实有一种方法可以提交所有更改。 263 | 如果您执行git commit -a,这会提交所有被Git跟踪的文件所做的更改。 264 | 因此,任何包含在先前快照中但自那时起已被修改的内容都不包括在内。 265 | 还有git add的变体。 266 | 例如,如果您执行git add :/,这将添加存储库顶部自上而下的所有内容。 267 | 但是在更高的层面上,我们之所以有git add和git commit之间的这种区分,而不是只快照整个目录,是因为经常有情况您不希望在当前的快照中包含所有内容。 268 | 例如,以下是一些示例。 269 | 其中之一是,我可能正在处理我的项目,然后实现了两个功能。 270 | 也许我不想有一个单独的快照,在这个快照之后,它会像“我实现了功能A和功能B”一样。 271 | 也许我想在历史记录中创建两个单独的节点,以便它看起来像我先实现了功能A,然后在此基础上实现了功能B。 272 | 因此,我有一个仅包含A的快照,然后下一个包含A和B。 273 | git add是一个工具,像暂存区一样是一个工具,它将允许我做这种事情。 274 | 275 | 另一个例子是,假设我正在修复一个 bug,我在我的代码中放置了 printf 语句,最终我找到了 bug,发现在某个地方有一个 +1,而那里不应该有 +1。 276 | 所以,我去修复了那个问题,然后想要使用我的修复程序创建一个新的快照,但是这个快照可能不应该包括我所有的打印语句。 277 | 它只需要包括去掉那个 +1 的修复。 278 | 所以,我可以手动删除所有的打印语句,但 Git 有更好的解决方法。 279 | 实际上,有一种方法可以指定我只想添加删除那个 +1 的更改,然后我就可以提交它,拍摄新的快照,然后可以丢弃所有其他更改。 280 | 有一些命令可以做到这一点,其中一些在讲义中有链接。 281 | 这些是使用暂存区帮助你的两种方式,也是为什么没有像“快照全部”这样的命令。 282 | 是的,约翰指出另一个例子是,你可能在当前目录中有日志文件,在运行程序时会运行这些日志文件,你可能不想在拍摄快照时包括它们。 283 | 还可能有其他一些东西,比如如果编译你的项目,你最终会得到一堆.o和类 ELF 文件。 284 | 你可能不想让这些成为你的历史的一部分。 285 | 所以,回到之前我给你展示的内容,我将清除终端屏幕,然后向你展示 git log 命令。 286 | 所以,git log 允许你可视化版本历史记录,这是一个非常有用的命令。 287 | 默认情况下,git log 显示版本历史记录的一个平面化版本。 288 | 所以,即使版本历史记录是一个图形,这也会将其线性化,并按顺序显示事物。 289 | 我个人觉得这很困惑,所以我几乎从不使用 git log。 290 | 相反,git log 接受一些参数,实际上将历史记录显示为一个图形。 291 | 所以你现在可以把它看作是一个魔法咒语,如果你想弄清楚每个标志的确切作用,你可以阅读文档。 292 | 但现在,这看起来并没有那么不同,因为我们的图形只有一个节点。 293 | 所以,将其可视化为一个平面化的东西和一个图形并没有太大的区别。 294 | 让我创建一个新的快照,然后我们可以再次运行这个命令,看看它到底做了什么。 295 | 296 | 那我将在hello.txt中加入另一行,如果我使用cat hello.txt命令,它将显示之前的内容再加上新加的内容。 297 | 我可以使用git commit命令,但会发现似乎什么都没有发生。 298 | 它只会显示 "no changes added to commit" 或 "no changes staged for commit." 为什么呢?因为我没有将新加的内容加入到staging area(暂存区)。 299 | 我没有告诉git,"这是应该包含在下一次快照中的内容"。 300 | 因此,如果我运行git add hello.txt命令,git status将显示"好的,这个修改已准备好被提交了。 301 | "现在我可以运行git commit命令了。 302 | 我会输入一个无用的提交消息,然后新的更改就完成了。 303 | 这样,我的历史记录中就有了另一个节点,这个节点有一个在屏幕上显示的哈希值。 304 | 如果我重新运行之前的命令,即带有所有这些参数的git log命令,它实际上开始看起来更像一个图。 305 | 在这里,你会注意到这就像那个图被旋转了90度一样。 306 | 更近期的提交被垂直地显示在顶部。 307 | 这里显示了一个提交,包括提交哈希值、一些元数据和提交消息。 308 | 下面我要谈论的是这一部分。 309 | 你记得我们之前谈论过对象,如存储库中实际内容,然后谈论了引用,即使用人类可读名称命名存储库中的内容。 310 | 当你初始化一个git仓库时,默认情况下会创建一个名为"master"的引用。 311 | 根据约定,它通常表示代码中的主开发分支。 312 | 所以"master"将代表项目的最新版本。 313 | 在这里,你可以将"master"视为指向这个提交的指针。 314 | 随着我们添加更多的提交,这个指针将被改变,指向后面的提交。 315 | 我们还看到"HEAD"。 316 | 这是git中的一个特殊引用。 317 | 它类似于"master"引用,但它具有一些特殊的作用。 318 | "HEAD"基本上用于指向你当前正在查看的内容。 319 | 有问题吗?是的,问题很好。 320 | 问题是,"你以前用过GitHub吗?你必须创建一个账户才能使用它。 321 | GitHub和git有什么关系?"答案是,GitHub是git的一个仓库托管平台。 322 | 你可以在GitHub上创建一个账户,并将git仓库存储在那里,以便与其他人进行协作。 323 | 但作为命令行工具的git与GitHub是独立的。 324 | 325 | 所以,你不必使用Github来使用Git。 326 | 你也不必使用Github来声明Git,就像其他Git存储库的提供者,例如Bitbucket或GitLab之类的,所以是的,Github是Github存储库的主机。 327 | 还有其他问题吗?是的,问题是如果您想要将此存储库放在Github上,该怎么做?是的,有一组单独的命令来完成这个任务。 328 | 有一个概念,即将本地版本历史记录副本与另一个副本进行交互,因此另一个副本称为远程副本,然后有一组与Git远程副本交互并将数据从您的副本发送到Git远程副本并将数据从Git远程副本获取到您的本地副本的命令,我们稍后在本讲座或讲义中介绍。 329 | Ron可能会制作一个补充视频来配合这个讲座。 330 | 还有其他问题吗?好的,还有几个基本命令要向您展示。 331 | 到目前为止,我向您展示了版本历史记录,并且我们已经拿出了一个文件并进行了修改,但我们除了阅读消息之外还没有真正利用历史记录。 332 | 一个有用的Git命令是被称为Git checkout,这是一种有点奇怪的命令。 333 | 它让您做很多不同的事情,但其中一个是让您在您的版本历史记录中移动。 334 | 因此,我可以给Git checkout之前的提交哈希值,而且我不需要输入整个哈希值,我可以给它一个前缀,让它弄清楚我在说什么。 335 | 这将改变我的工作目录的状态,使其与该提交时的状态相同。 336 | 因此,在这里,如果我运行cat hello.txt,回想一下,我之前只有一个行,而现在我添加了第二行。 337 | 现在,如果我运行Git log命令(该命令非常有用,可以显示所有内容),请注意,此输出看起来与之前有些不同。 338 | 我的实际历史内容,提交本身以及它们之间的关系等方面都没有改变,但是参考文献已经改变了。 339 | 请注意,HEAD在这里,即使是master也在这里。 340 | 因此,在高层次上,这告诉我这是我现在正在查看的内容。 341 | 如果我想回到这里,我可以键入Git checkout和这个提交哈希值。 342 | 有没有人知道我可以在这里输入不同的东西,而不是这个长哈希值,以便返回到这个提交?是的,你可以给它这里以绿色标记的分支名称,并且它引用这个提交。 343 | 所以我可以给它一个短名称或可读性更强的名称,现在如果我运行cat hello.txt,注意到它有第二行内容。 344 | 是的,是的,为了重复一遍,Git checkout 实际上会改变你的工作目录的内容,因此如果你滥用它,它可能是一个有些危险的命令。 345 | 例如,你可以看到如果我修改hello.txt然后尝试从之前的Git checkout 命令,实际上在这里注意到它显示错误。 346 | 它说有一个文件已经被修改了,Git checkout 将会销毁你的修改。 347 | 你可能想对此做些什么,但是有一些标志,例如Git checkout -f,这样强制执行,现在它正在丢弃我的更改。 348 | 所以是的,Git checkout 有可能,嗯,它确实会修改你的工作目录,并且如果你不小心,它可能会实际上销毁更改。 349 | 350 | 问题:没错,是的,这正是我想让你们思考的,这些类似疯狂的 git 接口命令是如何对应于对图形的变异和对引用或添加到图形中的引用映射的变异的。 351 | 所以没错,git checkout 移动了头指针,然后还通过指向的头指针的内容改变了您的工作目录的内容。 352 | 当然,这是我对该提交的名称。 353 | 还有其他问题吗?好的,所以我想向您展示的另一个基本命令是 git diff 命令。 354 | 我要修改这个文件并对其进行一些更改。 355 | git diff 命令可以显示自上次快照以来发生了什么变化。 356 | 这对于了解您的项目正在发生什么很有帮助。 357 | git diff 还可以采用额外的参数,例如您可以进行 git diff 并说计算一个差异不是相对于最后一个快照、最后一个提交,而是相对于此时说,“好的,自此时起已添加了两行到 hello.text。 358 | ”问题:所以,您的问题是如果没有此处的额外参数,该命令会做什么?这是个好问题。 359 | 它是计算与头文件有关的差异,并查看我的 git 日志指向的是什么。 360 | 所以它正在相对于此提交进行 git diff,您可以明确指定。 361 | 您可以使用 git diff head hello text。 362 | 好的,是的,这是一个好问题。 363 | 就像您当前的位置一样,头文件是指向的最后一个快照,所以对于 hello.text 文件的变化,可能与头文件不同。 364 | 为了澄清,头文件是指向最后一个快照的,就像我这里的图片一样,头文件和主分支都在这里,当前工作目录有点独立于此。 365 | 例如,如果您在此处删除了所有文件,则不会更改历史记录图或引用,所以是的,您可以在此处和此处之间存在差异,从高层次来看,这就是您在项目上工作的方式。 366 | 例如,您在此处进行了一些更改,将其添加到暂存区,然后进行提交,这将在此处创建一个新的快照。 367 | 还有其他问题吗?问题是,Git 是否确实以显而易见的方式保存所有这些东西,还是做了一些花哨的事情?答案是,它确实做了一些更高级的事情,但您可以使用接口来认为它是以那种方式存储的。 368 | 实际上,Git 使用 Delta 压缩,还进行了一些其他操作,但是在磁盘上的表示实际上是相当高效的。 369 | 问题:这是一个好问题。 370 | 所以问题是,我们正在将当前工作目录与过去的特定快照进行比较。 371 | 我们能否将两个快照进行比较,例如在历史上的两个不同时间点?是的,git diff可以在此处使用另一个参数。 372 | 例如,我可以将head与这里进行比较,我可以比较从这里到head在hello文本中的变化,它会显示我在其中添加了第二行。 373 | 还有其他问题吗? 374 | 375 | 是的,问题是,你正在一个Dropbox文件夹中共同开发一个项目,任何人都可以迁移到Git。 376 | 将Dropbox文件夹变成Git仓库是否有意义?不要在Dropbox内部使用Git。 377 | Dropbox会破坏您的Git仓库。 378 | 有很好的解决方案。 379 | 一个是只使用Github。 380 | 否则,在课后与我交谈,有安全使用Dropbox作为Git远程的方法。 381 | 还有其他问题吗?接下来,我们将讨论Git的另一个强大功能——分支和合并。 382 | 您在自己的项目上工作和与他人合作时,几乎肯定会使用它。 383 | 在这一系列演示中,我们将编写一个简单的计算机程序,而不是使用简单的文本文件,因为它将更好地说明分支和合并的概念。 384 | 当我们进行演示时,我们将牢记Git界面命令与底层数据模型之间的连接,与对象和引用的连接,以及这些命令如何修改这两个数据结构。 385 | 让我执行'git status'来查看我的仓库的当前状态。 386 | 在这里,我修改了hello文本。 387 | 实际上,我不再关心这个修改。 388 | 这是一个随意的文件。 389 | 如果我执行'git checkout folio text',这是'checkout'命令的另一种不同用法,它基本上会丢弃我在工作目录中所做的更改,并将hello文本的内容设置回它指向的快照的状态。 390 | 如果我喜欢它,'git log --all --graph --decorate'将显示我在这里添加了初始文本,并在这里添加了单行。 391 | 因此,现在,'hello文本'没有我添加的第三行。 392 | 它只有原始内容。 393 | 下一次,我们应该编写一个非常简单的程序。 394 | 我们将称这个程序为'animal.py',让我写一个程序,当我运行它时,它会输出一点内容。 395 | 让我们看看。 396 | [掌声]所以当我运行这个程序时,它运行'main',调用'default',然后让我继续定义'default'。 397 | '默认'只会打印'hello'。 398 | 所以这是一个向用户打招呼的程序。 399 | 如果我运行'animal.py',我会看到它只是打印'hello'。 400 | 401 | 那么这将是我们的起点。 402 | 如果我执行 "git status",它会告诉我 "animal.py" 是一个未被跟踪的文件。 403 | 开始,我想让它成为我的快照的一部分,所以我要执行 "git add animal.py" 将其添加到暂存区,然后执行 "git commit"。 404 | 这里,我将写另一个无用的提交信息。 405 | 实际项目中不要写这样的提交信息,但是现在这样做还可以。 406 | 所以现在我有了这个基本的 "animal.py",如果我查看我的 "git" 历史记录,现在我有了这个最新的快照。 407 | 这是提交哈希,这是主分支指向的地方。 408 | 现在我们实际上是在演示如何使用 Git 分支来实现平行的开发线路。 409 | git branch 命令或分支子命令用于访问与分支相关的功能。 410 | 仅运行 git branch 本身会列出本地存储库中存在的所有分支。 411 | 它还可以接受一个额外的 -v 参数,以更详细的方式打印一些额外的信息。 412 | 如果我们执行 git branch,然后指定新分支的名称,Git 将创建一个新的分支,该分支只是一个指向我们当前正在查看的地方的引用。 413 | 所以现在有一个新的引用叫做 "cat",它指向 HEAD 指向的地方。 414 | 如果我再次查看 git log,我会看到 HEAD 指向主分支,主分支在这里,这也是猫分支的位置。 415 | 所以现在我有了两个分支,两个引用指向同一次提交。 416 | Git 不仅知道我们当前正在查看历史记录中的哪个快照(所以 HEAD 指向这个提交),而且还知道 HEAD 与分支有点关联。 417 | 在这里,HEAD 与主分支关联,如果我创建一个新的快照(如果此时键入 git commit),将创建下一个快照,并将指向该新快照。 418 | 主分支也将随着 HEAD 更新。 419 | 如果我执行 git checkout cat,它会切换到 cat 分支。 420 | 它将工作目录的内容替换为 cat 所指向的内容,这种情况下与之前的内容相同。 421 | 但现在,如果我再次查看 git log,现在 HEAD 指向 cat 而不是主分支,然后主分支也指向同一位置,同一次提交。 422 | 现在,如果我对当前的工作目录进行更改并进行新的提交,cat 分支指针将被更新以指向新的提交,而主分支将继续指向之前指向的位置。 423 | 所以让我修改 animal.py 添加一些与猫相关的功能。 424 | 那么我将会这样说,如果sys.argv[1]是“cat”,那么就运行cat()函数,否则运行默认函数。 425 | 然后让我继续导入/定义cat()函数。 426 | 所以猫不说“hello”,他们说“meow”——非常简单。 427 | 现在,如果我运行animal.py并给它cat参数,它会输出“meow”。 428 | 429 | 如果我给它其他的参数,它就会默认回到"hello"。 430 | 所以我做了一个简单的改变。 431 | 如果我运行git status,它会显示animal.py已经被修改了。 432 | git diff会显示自上次提交以来的改变。 433 | 在这里,我添加了这个绿色高亮显示的cat()函数,然后稍微改变了main()函数。 434 | 现在,如果我运行animal.py并给它cat参数,它会说"meow"。 435 | 现在,如果我们给它其他的参数,它就会默认回到"hello"。 436 | 所以我做了一个简单的改变。 437 | 如果我运行git status,它会显示animal.py已经被修改了。 438 | git diff会显示自上次提交以来的改变。 439 | 在这里,我添加了这个绿色高亮显示的cat()函数,然后稍微改变了main()函数。 440 | 现在,如果我运行git add animal.py,git commit(实际上你应该写一个稍微更有用的提交消息,比如"添加cat功能"),现在如果我查看git log,我会看到更多的内容。 441 | 我要向你展示这个git log命令的一个参数——--oneline(一行,正确拼写),它会显示一个更紧凑的图形表示。 442 | 这应该是一个更有用的东西,因为我们现在在屏幕上缩小了很多,没有足够的空间显示一个长的提交历史。 443 | 所以在这里,我们看到提交的顺序仍然是线性的,而且我们的主分支(master)仍然指向它之前指向的位置。 444 | 在这里,我们只有基本的动物顶级高功能,但现在我们有了这个cat分支,它添加了cat功能。 445 | 例如,我们可以使用checkout master来回到主分支,然后在这里查看animal.py,它不再具有cat功能。 446 | 如果我们查看git log,我们会看到head指向主分支,因此我们可以在不同的开发分支之间来回跳转。 447 | 448 | 现在我们有了cat功能,假设我们想同时添加狗功能。 449 | 假设在这种情况下,像cat功能一样,狗功能也在开发中,或者可能有其他人在工作,所以我们只想从基本的主分支提交开始,从那里开始构建狗功能。 450 | 那么我现在想做什么?我想为添加狗相关功能创建一个新的分支,称为dog,然后稍后将其合并。 451 | 所以我可以使用git branch dog命令,然后使用git checkout dog命令创建一个新的dog分支,然后将其检出。 452 | 实际上,这有一个简短的形式,git checkout -b dog。 453 | 那么现在我用“git branch dog”命令创建了一个新的“dog”分支,然后使用“git checkout dog”命令将其切换到该分支。 454 | 如果我现在查看我的图表,就会看到猫在原来的位置,主分支也在原来的位置,但现在HEAD不再指向主分支,而是指向这个新创建的“dog”引用,该引用也指向同一个提交点。 455 | 因此,在这个基础提交点上,我将添加我的“dog”功能。 456 | 现在,让我去定义我的“dog”函数,因为狗不说“hello”,它们说“woof”。 457 | 然后,我将添加一些类似的功能,以决定是运行默认还是狗功能。 458 | 因此,如果第一个参数是“dog”,那么我要运行狗功能,否则,哎呀,否则我要运行默认功能。 459 | 这是我相对于主分支指向的基础提交点所做的更改。 460 | 因此,我已经添加了狗功能,并且稍微修改了主函数,这是对我在猫分支中所做修改的一种并行修改。 461 | 让我去执行“git add animal.py”命令,将其添加到暂存区中。 462 | 463 | 如果我运行 "get status" 命令,我会发现在我进行下一次提交时,这个修改会被提交。 464 | 然后我运行 "get commit add functionality" 命令。 465 | 现在,当我查看 Git 图形时,与之前我们看过的那些图形相比,它实际上看起来很有趣。 466 | 这表明这三个提交与之后的提交是共同的,但是在此之后,历史记录实际上分叉了,在一条开发线上添加了猫功能的一个提交,以及在另一条开发线上添加了狗功能的另一个提交。 467 | 然后,使用 "get checkout" 命令,我可以在狗、猫和主线之间来回切换。 468 | 这很棒,我可以在不同的功能上并行开发,但只有当我最终将这些内容合并回我的原始开发线时,才真正有用,以便将两个功能合并到我的源代码的单个版本中。 469 | 用于执行此操作的命令是 "get merge"。 470 | 所以可以认为 "get branch" 和 "get merge" 是相反的。 471 | 让我先检出 "get checkout master",然后我要将猫功能和狗功能合并到主线上。 472 | 为了做到这一点,我可以使用 "get merge" 命令。 473 | Git 合并实际上相当高级,我实际上可以同时合并猫和狗。 474 | 但是对于这个演示,我们将一次只合并一件事。 475 | 所以首先,我将输入 "get merge cat",然后它会给我们一些内容。 476 | 它说“快进”。 477 | 那这里发生了什么?嗯,这是 Git 可以做的有趣的一件事。 478 | 当你在一个特定的提交上,并将某个其他分支合并到该提交的前任分支时,不需要创建任何新的快照或做任何其他高级操作。 479 | 基本上,这个主线分支,在这个提交的指针,可以直接移动到这里,以合并那个猫功能。 480 | 所以如果我们再次查看 Git 日志,我们可以看到主线基本上指向与猫功能相同的地方。 481 | 好了,现在我们在主线上,并且它有了猫功能。 482 | 很好,我们完成了一半。 483 | 如果我们查看 "animal.py" 文件,它具有猫功能,但缺少狗功能。 484 | 所以接下来让我们尝试 "get merge dog" 命令。 485 | 这次发生了一些更有趣的事情。 486 | 所以这一次,该分支无法像之前那样快速前进。 487 | 这不是一个比另一个严格更旧的东西。 488 | 489 | 存在一种并行开发方式,可能与当前的一系列变化不兼容。 490 | 它会尽力自动合并来自另一个分支的更改,因此它会显示"正在自动合并animal.py"。 491 | 但在这种情况下,存在所谓的合并冲突。 492 | 因此,它无法自动解决这两个并行开发分支之间的所有冲突。 493 | 当您在实际的软件项目中工作时,您会看到这种情况,因为会出现一些复杂的、略有不兼容的并行变化。 494 | 因此,现在需要由开发人员来解决此问题。 495 | Git提供了一些功能来帮助解决合并冲突。 496 | 有一个叫做git merge tool的程序,在我的特定设置中,这将启动Vimdiff。 497 | 实际上,这未配置好。 498 | Vimdiff会启动正确的程序。 499 | 让我设置我的Git来实际启动正确的工具。 500 | 让我们跳过这部分,手动查看此事件。 501 | 有一个叫做Vimdiff的程序,可以在键入"git merge tool"时启动它,这是当您尝试进行git merge时出现合并冲突时使用的工具。 502 | 但在这种情况下,我们将手动解决它们。 503 | 因此,我执行了"git merge --abort",这将我放回到我尝试git merge之前的状态。 504 | 这是我的仓库当前的状态。 505 | 我回到了主分支与猫分支相同的位置,并将狗分支合并到主分支中。 506 | 因此,我执行了git merge dog命令,它显示"在animal.py中发生合并冲突"。 507 | 因此,让我们直接查看animal.py文件。 508 | 看起来顶部部分看起来很合理,它具有我想要的猫和狗函数。 509 | 但现在我在main函数中看到一些奇怪的东西,这就是我添加的略有不兼容的更改。 510 | 因此,在这里,它会显示在一个位置,即您所在的分支上,您有这个内容。 511 | 而在您尝试合并的分支上,它有这个内容。 512 | 然后,这些尖括号和等号是冲突标记。 513 | 因此,这就是您所在的位置和您正在尝试合并的内容,它基本上是在一个情况下是这个,另一个情况下是那个,它不知道如何解决这两个内容。 514 | 515 | 这需要程序员来解决这个问题。 516 | 在这种情况下,我们可以删除冲突标记,然后发现我们实际上可以将这段代码连接在一起并得到正确的结果。 517 | 也许我们想做一个小改变,比如把这个应该是if,这个应该是else-if,这个应该是else。 518 | 这样可能更有意义,实际上我认为这是必要的正确性。 519 | 因此,程序员需要稍微修改一下代码,使其在合并在一起时变得有意义。 520 | 但是一旦程序员解决了合并冲突,解决了冲突标记之间的问题,就可以保存这个文件,然后我们可以执行git merge --continue来告诉git我们已经解决了问题。 521 | 需要重新添加animal.py文件以告诉git我们已经解决了这些问题。 522 | 然后我们需要git merge --continue。 523 | 它会弹出一个编辑器,我们可以为即将创建的新提交提供提交消息。 524 | 现在,如果我们查看git历史记录,我们有一个表示我们刚刚创建的合并提交的单个提交,其中包括狗功能。 525 | 这里,它的父节点是狗提交和猫提交。 526 | 因此,从这一点向后,我们的历史记录中都包含这两个分支,并且当前提交包含了这些分支中开发的所有功能。 527 | 因此,如果我们运行animal duck fight with cat,它会执行猫的操作。 528 | 如果我们使用狗运行它,则会执行狗的操作。 529 | 如果我们使用其他任何东西运行它,则会回退到默认实现。 530 | 这是如何在不同分支上进行开发并使用合并命令和git解决这些不同分支并将它们组合成包含所有并行开发的功能的单个快照的演示。 531 | 在进行git分支和合并时,可能会出现合并冲突,这些冲突会显示为文本文件中的冲突标记。 532 | 您可以手动解决它们,git也有一些工具可以帮助解决这个问题,尽管这些工具有点高级,我们只会在讲义中提到它们,而不是实际演示它们。 533 | 所以这就是git分支和合并。 534 | 还有什么问题吗?没有吗?好的。 535 | 那么接下来,我们将讨论git远程。 536 | 这基本上是如何使用git与他人协作。 537 | git仓库中包含的内容,即.git文件夹,代表了历史的完整副本。 538 | 539 | 它包含对象和引用,包含了所有之前的快照。 540 | 使用Git与其他人协作的方式是其他人也可以拥有整个Git仓库的副本。 541 | 然后,你的本地仓库可以知道同一仓库的其他克隆副本的存在,这是一个被称为远程仓库的概念。 542 | 使用git remote命令可以列出当前仓库所知道的所有远程仓库。 543 | 在我们的仓库中,由于我们还没有配置任何远程仓库,所以这个命令什么也不会输出。 544 | 它只知道我们正在使用的单个本地仓库的副本。 545 | 但是实际上,如果你与其他人合作,你的Git可能会知道GitHub上的代码副本。 546 | 然后有一系列的命令将你本地仓库的更改发送到Git所知道的远程仓库,比如将东西从你的电脑发送到GitHub。 547 | 还有另一系列命令,用于从本地仓库获取已进行的更改,从GitHub获取更改并将其应用到你的本地副本中。 548 | 在这个演示中,我们不会配置GitHub账户,登录并在那里创建新仓库。 549 | 你可以找到其他的教程来完成这个任务。 550 | 我们将只使用同一台计算机上的另一个文件夹,并将其视为Git远程仓库。 551 | 让我来到演示文件夹,在上一级目录中创建一个名为"remote"的新文件夹。 552 | 然后使用git init --bare在这里建立仓库。 553 | 这些命令在日常使用中可能用不到。 554 | 但现在,我已经将"remote"文件夹设置为适合用作Git远程仓库的文件夹。 555 | 现在回到我的演示文件夹,我的Git仓库,我可以使用git remote命令列出远程仓库。 556 | 暂时还没有任何内容,但是我可以使用git remote add功能让我的本地仓库知道远程仓库的存在。 557 | 因此,我可以使用git remote add命令,然后远程仓库具有名称和URL。 558 | 在这种情况下,我将使用名称origin,这是约定俗成的远程仓库名称,如果你只使用一个仓库。 559 | 560 | 然后对于URL,通常这将类似于Github URL或Bitbucket URL或Gitlab URL,如果您使用在线存储库托管服务。 561 | 但在这种情况下,它只是指向我本地计算机上的一个文件夹的路径。 562 | 在父目录中有一个名为remote的文件夹,它将作为此存储库的git远程操作。 563 | 所以现在,一旦我完成这个步骤,就有一组与此远程进行交互的命令。 564 | 其中一个有用的命令是git push命令。 565 | 此命令可以将更改从您的计算机发送到远程主机。 566 | 该命令的格式是git push接受远程名称,然后接受本地分支名称,远程分支名称。 567 | 它会创建一个新分支或更新远程上的一个分支,并将其设置为此处指定名称的内容。 568 | 所以,具体使用它的方法可能是像git push这样。 569 | 我只有一个名为origin的远程,那我应该推送什么呢?让我看看我的历史记录图。 570 | 我有一堆东西可以推送。 571 | 让我从我的本地计算机master推送到origin。 572 | 所以,我想在远程计算机上创建一个名为master的分支,它将与我的本地计算机上的master分支相同。 573 | 所以,让我继续运行这个命令。 574 | 它会打印一些东西,并说:“在远程上,我创建了一个名为远程master的新分支,它指向我的本地计算机上的master分支的相同位置。 575 | ”现在,如果我执行git log,它会显示给我。 576 | 蓝色是head,是我当前所在的位置。 577 | 绿色是我本地git存储库中的所有分支。 578 | 现在,我们看到了一种新的颜色,我们之前没有见过的。 579 | 红色是git显示的我本地副本所知道的远程参考。 580 | 所以,在remote origin上,还有一个名为master的分支,它恰好指向与我本地master分支相同的位置。 581 | 因此,现在,如果我对我的本地副本进行更新,比如说我在这里更改了这些内容的大写,然后运行git add animal.dot.hi,git commit,在这里有一个带有消息的短表单提交,所以它不会弹出编辑器。 582 | 我会给它一个晚期提交的消息。 583 | 现在,如果我查看git图,现在我看到我创建了这个新的快照,它有这些小写字母的东西,但是origin master仍然停留在这里。 584 | 所以,如果其他人查看远程,他们只能看到到此为止的更改,我们可以实际演示这个功能。 585 | 586 | 好的,让我打开一个新标签页,进入我的 playground 目录。 587 | git clone 命令是一个可以用来从某个存储库的副本开始,并创建自己的本地副本的命令。 588 | 因此,当使用 git 存储库时,通常使用这个命令。 589 | 例如,可能有一些在 Github 上可用的东西,你想把它全部复制到你的机器上以查看它或开始进行开发。 590 | 因此,git clone 的格式是接收一个 URL,然后接收一个文件夹名称作为克隆的位置。 591 | 在我们这里,我们只是从这个远程目录克隆。 592 | 我们假装这个远程文件夹实际上是一个远程机器,然后我们把它全部克隆到叫做 demo two 的文件夹里。 593 | 克隆到 demo 2 完成后,我要 CD 到那个目录。 594 | 然后现在,在这里我将重命名底部的标签。 595 | 我会说这个是 machine one,这个是 machine two。 596 | 因此,你可以将这两个视为位于不同机器上的两个不同的人,他们拥有自己的存储库副本,并与单个远程进行交互。 597 | 因此,如果我在 machine one 上执行我一直在做的 git log 命令,我会在 machine two 上看到历史记录的这部分。 598 | 因此,machine 2 上的 master 指向的是与 origin master 相同的位置,并且它说:“合并分支 dog”。 599 | 因此,如果我在这里查看 animal.dot.pie,即使在 machine one 上我有这个新提交,而这个提交只存在于这个机器上,而不在远程和 machine two 上,它也没有这些更改。 600 | 因此,如果我想修复这个问题,如果我想将这些更改发送到远程,例如将其发送到 GitHub 或持有或维护源代码的机器上,我可以使用 git push 命令。 601 | 同样,git push origin master : master 可以工作,但是每次想要这样做时键入这个命令有点麻烦。 602 | 像这样的操作非常常见,因此 git 有一种方法使它变得更简单。 603 | 它有一种方法来维护你自己本地机器上的分支和远程机器上的分支之间的关系。 604 | 它有一种方法来知道本地分支对应的远程机器上的分支,这样你就可以输入 git push 的缩写版本,它会知道扩展形式的所有参数是什么。 605 | 有几种不同的语法来做到这一点。 606 | 一种方法是使用 git branch --set-upstream 命令,它为当前已检出的分支(即 master)设置上游分支,我将输入 origin master。 607 | 现在可以看到,它说“分支 master 已设置为跟踪来自 origin 的远程分支 master”。 608 | 现在,如果我输入 git branch -VV(记住,这会以详细的方式告诉我所有我知道的分支,这就是 -VV 的含义),我在机器一上的本地机器上有三个分支。 609 | 我有 cat、dog 和 master,我的本地机器上的 master 对应于 origin master。 610 | 所以现在我只需要输入 git push,而不需要所有额外的参数。 611 | 我本来可以用 git push origin master 冒号 master,但这是不必要的。 612 | 613 | 现在它会显示“设置跟踪远程分支 master from origin 的主分支”。 614 | 如果我键入 git branch - VV(记住,这是以非常详细的方式告诉我所有已知分支的信息,这就是 -VV 的含义),我在一台机器上的本地机器上有三个分支。 615 | 我有 cat,dog 和 master,本地机器上的 master 对应于 origin master。 616 | 因此,现在我可以只键入 git push,而无需添加所有额外的参数。 617 | 我可以使用 git push origin master 冒号 master,但这不是必要的。 618 | 619 | 620 | 它会知道我想要推送到 origin master,并进行更改。 621 | 所以现在这些更改在远程存在。 622 | 我们可以转到机器二,假装我们是与此存储库交互的其他人,如果我执行 git log 命令,我仍然看不到更改。 623 | 那么这里发生了什么?嗯,为了运行一个单独的命令,这是必要的。 624 | 或者说,为了使这些更改在这里存在,必须运行一个单独的命令。 625 | 默认情况下,所有 git 命令都不会与互联网通信。 626 | 它全部在本地工作,这意味着它运行非常快,但是需要特殊命令才能说您想要检索在其他地方进行的更改。 627 | 用于执行此操作的命令称为 git fetch。 628 | Git fetch 将远程名称作为参数,但如果只有一个,则会直接使用它。 629 | 因此,您可以输入 git fetch,然后它将与此远程存储库通信,并表示远程有一些更新,我们可以通过运行 git log 来可视化它。 630 | 现在我们看到了另一种以前没有见过的情况。 631 | 我们在本地机器上有 master。 632 | 主分支不改变。 633 | git fetch 命令不会更改任何本地历史记录,本地引用(例如分支)。 634 | 但现在它知道 origin master 已更新为指向此新提交,并且有一个单独的命令,我们可以使用 git merge 来将 master 向上移动到此处。 635 | 或者还有一个称为 git pull 的命令,它相当于执行 git fetch,然后执行 git merge。 636 | 因此,如果我们在此处只执行 git pull,它将说它正在快进,正在将 origin master 合并到我们的 master 中,现在,如果我们查看 Git 历史图,我们目前已检出 master。 637 | 主分支指向我们所知道的 origin master 相同的位置,并且 Machine 2 和 Machine 1 之间的所有更改都已同步。 638 | 这些是与 Git 远程交互的基本命令。 639 | 因此,有用于列出远程主机并添加和删除它们等的 Git 远程命令。 640 | 然后有 Git push 命令用于将本地仓库的更改发送到远程仓库。 641 | 然后有 Git fetch 命令,用于检索存在于远程仓库上的更改,并在本地机器上获取这些更改。 642 | 一旦检索到这些更改,您可以使用 Git merge 将本地分支更新为指向远程分支所在的位置,或者您可以使用 Git pull 命令,它基本上是 Git fetch 和 Git merge 的组合。 643 | 当然,与所有这些命令分开的是我们之前讨论过的克隆命令,用于从远程仓库中获取一份副本并从该副本初始化本地仓库。 644 | 645 | 这就是与Git远程交互所使用的不同命令的快速概述。 646 | 这些命令有点复杂,需要一些时间来掌握所有不同的变体并理解它们在实践中的使用方式,但希望这能作为一个快速介绍,并且您可以看到不同的命令与底层数据模型的关系。 647 | 所有这些命令都只是从其他地方获取新对象或将对象从本地传输到其他地方,这些命令会改变引用。 648 | 因此,将Git的界面和一些这些设计不良的命令与底层数据模型联系起来,可以帮助您更好地理解它。 649 | 我们今天要讨论的最后一个主题是Git可以做的其他事情的概述,我们不会详细介绍如何执行这些操作,但我们只想告诉您,这些功能存在于您需要自己执行这些操作时。 650 | 您可以查找文档并找出如何准确执行它。 651 | 其中一个是Git config命令。 652 | 像我们看过的很多工具一样,例如shell、TMUX等,Git是高度可配置的,并使用纯文本文件进行配置,可以通过命令行界面进行编辑。 653 | 因此,Git config可以接受修改此文本文件的标志,或者您可以使用纯文本配置编辑home文件夹中的.gitconfig文件。 654 | 因此,对于本课程,我实际上剪切了大部分Git config,只留下了我的用户名和电子邮件,以供提交到Git的提交。 655 | 但是,您可以在此处放入很多内容,以使其更好地运作,并且您可以在线查找人们已配置其Git配置的不同方法。 656 | 通常,人们在其Git配置中具有文档,可以在GitHub上找到。 657 | 还有几个其他可能有用的随机命令。 658 | 其中之一是当您想要克隆使用Git clone的一个真正巨大的存储库时。 659 | 默认情况下,git clone会复制它正在下载存储库的远程的整个版本历史记录,但是您可以传递一个参数,即--shallow,这将避免这样做。 660 | 因此,如果在Github上有一些代码的副本,您想在本地计算机上获取副本,但该存储库真的很大并且具有10亿次提交,那么执行git clone --shallow将更快。 661 | 但是,当然,您将不会在本地计算机上拥有版本历史记录,您只会有最新的快照。 662 | 我们在实际软件项目开发中发现的另一个非常有用的命令是git add命令的交互式版本。 663 | 所以为了演示,我将对我的 animal.py 进行几个不同的更改。 664 | 我会在这里进行一些更改,更改一些文本,并在这里放置一个新的打印语句。 665 | 假设这第一个更改是我想要进行的真正更改,比如说是一个错误修复,而这里的另一个更改是我添加用于调试的 printf,但实际上我不想在下一个快照中提交它。 666 | 如果我执行 git diff,它会显示我已经进行了这两个更改,如果我执行 git add animal.py,它将为提交暂存这两个更改,而这不是我想要的。 667 | 我可以手动删除这个调试打印,然后执行 git add animal.py,但有一种更简单的方法。 668 | 有一个 git add -p 命令,它允许我交互式地为提交暂存文件的片段,因此有一些用于处理此功能的界面。 669 | 这里它问我是否要暂存这两个更改,而我不想。 670 | 671 | 但我要将它分成两个较小的更改。 672 | 这一个我想保留,所以我选择“Y”表示是,而这一个我不想要,所以我选择“n”表示否。 673 | 然后如果我执行 git diff --cached,这将显示为提交准备好的更改。 674 | 现在它只显示我想要保留的实际更改。 675 | 如果我执行 git diff,它仍然会显示我不想要保留的其他更改,这是我不想要的更改。 676 | 然后,我可以使用 git commit 命令指定一些提交信息,现在我只剩下这个更改,然后我可以使用 git checkout animal 来丢弃这个更改。 677 | 因此,交互式暂存的 git add -p 是一个有用的东西。 678 | 还有一些其他的命令,您可以自己查找 git blame 命令,因此该命令有点不祥,但可以用于确定谁编辑了文件的哪一行,您还可以找到负责修改该特定文件行的相应提交,然后您可以查找与其相关的提交消息等等。 679 | 因此,在我们当前的玩具存储库中执行此操作并不那么有趣,但我将转到班级网站的存储库,我们可以在此查看某个特定文件。 680 | 然后我可以看着这个,'为什么要添加这一行?它是什么意思?'我可以查看这个 git blame。 681 | 如果我执行 git blame config.yml,它将以正确的列打印出所有行,然后在左侧,它将显示该更改所做的提交和由谁做的。 682 | 然后查看此信息,如我可以查看到 collections 行,它是在此提交中进行的,这是最后一个修改该行的提交,现在我可以使用 git show 命令获取有关该特定提交的信息。 683 | 哦,这很有用-重做讲座是一个集合。 684 | 那可能与“集合”行有关。 685 | 除了显示提交和提交消息之外,它还向我显示了在该特定提交中引入的实际更改,我可以浏览它们并理解发生了什么。 686 | 另一个很酷的命令是一个称为 git stash 的命令。 687 | 因此,让我们回到我们的演示存储库并在此处演示。 688 | 假设有些更改在此处,我想暂时将它们放置,如果我执行 git stash,它将将我的工作目录恢复到上一次提交的状态。 689 | 因此,如果我执行 cat hello.txt,该更改已经消失,但它不仅仅被删除-它被保存在某个地方。 690 | 如果我执行 git stash pop,它将撤消这个储藏。 691 | 如果我执行 cat hello.txt 命令,那个更改就被删除了,但它并没有被彻底删除 - 它被保存在某个地方。 692 | 如果我执行 git stash pop 命令,它会撤消存储操作。 693 | 现在如果我查看 hello.txt 文件,它就会包含我所做的更改。 694 | 这是另一个有用的命令!另一个非常好用的命令是 git bisect,这个命令的界面比较复杂,我们不会详细演示。 695 | 但基本上,这是一个可以用来解决一堆需要手动搜索历史记录的问题的工具。 696 | 697 | 假设你在一个长期进行的项目中工作。 698 | 你有很多快照——你已经有了一千个提交——然后你发现某个单元测试不再通过。 699 | 但你知道这个单元测试一年前是通过的,而现在你正在尝试找出什么时候出了问题,也就是你的代码中什么地方出现了回归。 700 | 你可以手动检查——比如说,回退到前一个提交,看看单元测试是否仍然失败,回退到前一个提交,看看是否仍然失败,最终你会找到第一个测试停止工作的提交,它可能会告诉你什么出了问题。 701 | 但这样手动做有点烦人。 702 | git bisect可以自动化这个过程,它实际上是对你的历史进行二分查找,因此它是以最有效的方式来进行查找的。 703 | 而且,git bisect可以接收用于尝试确定它正在查看的提交是好还是坏的脚本。 704 | 因此它可以是一个完全自动化的过程。 705 | 比如说,你可以给git bisect一个单元测试,然后说,“找到这个单元测试停止通过的第一个提交。 706 | ”这是一个非常强大的工具。 707 | 另一个随机但有用的东西是gitignore文件。 708 | 默认情况下,如果在一个目录中有随机的文件,比如说让我创建.DS_Store文件——哎呀,创建.DS_Store文件,然后执行git status。 709 | .DS_Store是Mac OS创建的一些讨厌的文件。 710 | 我不知道里面具体有什么内容,但基本上,一旦这个文件在这个目录中,现在每当我执行git status时,它会说,“噢,这里有一个我从未听说过但显然在这里的新文件——你要添加它吗?”这样的东西会让人感到烦恼。 711 | 除了特定于操作系统的垃圾之外,可能还有许多其他的东西存在于一个目录中。 712 | 例如,如果你正在处理C代码,你可能会编译它并生成.o文件或可执行文件等等,你可能不希望二进制文件成为你提交历史的一部分,你只需要源代码。 713 | 因此,Git有一种方法让你告诉工具你不关心特定的一组文件并忽略它们,这就是所谓的gitignore文件。 714 | 因此,如果我修改当前目录中名为gitignore的文件,我可以指定特定的文件名或文件名模式,比如说,我可以指定任何以.o结尾的文件,以及.DS_Store。 715 | 现在,如果我执行touch food.o,然后执行git status, 716 | 我会确保 git 命令显示为“OK”,我已经清空了税务,做了修改,并且我已经在 get ignore 中添加了文件。 717 | 所以,你应该使用它来跟踪你的 git ignore 文件。 718 | 但是请注意,它没有提到当前目录中存在的我的点 d s store 文件或 my food out o 文件,因为它们已经被忽略了。 719 | 这是关于一些高级 git 功能的简要概述,只是为了让你了解这个工具可以做哪些酷炫的事情。 720 | 最后,我们还有一些其他主题在讲义中有更详细的介绍。 721 | 我会在这里快速列出它们,以便你知道要查找什么。 722 | 其中一个是有许多图形化客户端可用于 git。 723 | 我们个人不使用它们;我们喜欢 git 命令行工具,但其中一些还不错,你可能想要查看一下,看看你是否喜欢使用它们。 724 | 另一个是 shell 集成。 725 | 你已经注意到在这个教程中我一直在使用 git status 命令来查看仓库的情况。 726 | 好吧,这有点烦人,很多人都会设置他们的 shell 提示,以便在每一行中,它将向我显示一个非常简洁的仓库情况摘要。 727 | 因此,它可能会向我显示我当前检出的分支的摘要,以及我是否修改了文件或未跟踪的文件。 728 | 因此,我们在讲义中提供了一个链接,介绍了如何获取漂亮的 shell 集成,以便在 shell 提示中显示与 git 相关的信息。 729 | 与此类似,你可以获得与你的文本编辑器的集成。 730 | 例如,我使用 vim,我有一个 vim 插件,可以做所有有趣的与 git 相关的工作。 731 | 我可以使用这个插件查看 git blame 信息。 732 | 记住,我们刚刚通过命令行查看了这个信息。 733 | 相反,我可以使用这个插件查看它,并且它可以让我更快地工作。 734 | 我可以查看 git blame,当我在悬停在特定提交上时按回车键,它会在我的文本编辑器中显示那个特定的提交。 735 | 它甚至会隐藏所有其他文件,只显示我正在查看的那个文件,这可能是我关心的内容。 736 | 因此,我们在讲义中也提供了链接。 737 | 如果你感兴趣,还有其他一些有趣的东西可以查看。 738 | 最后,单独这个讲座可能不足以教会你所有需要了解的关于 git 的内容。 739 | 这是一个良好的开端。 740 | 我们认为学习 Git 的正确方式是先了解底层的数据模型,包括整个对象和引用系统,以及 Git 是如何模拟历史的。 741 | 然后我们向你介绍了使用 Git 命令的基础知识。 742 | 如果你想真正熟练地掌握这个工具,在今天的讲义资源部分中,我们有一本叫做《Pro Git》的书籍链接。 743 | 这是一本免费的书,写得很好,篇幅也不长。 744 | 我认为,阅读该书的前几章,你基本上就能掌握使用 Git 进行真实软件项目和贡献的所有知识,包括在 GitHub 上进行项目开发等等。 745 | 最后,就像所有其他讲座一样,如果你想找一些有趣而具有挑战性的问题,可以尝试一下我们提供的一些练习。 --------------------------------------------------------------------------------