一、The shell
- 终端解析命令时是根据$PATH环境变量去搜索的,例如
missing:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- 在程序间创立连接,使用< file 和> file,可以将程序的输入输出流分别重定向到文件:
missing:~$ echo hello > hello.txt
missing:~$ cat hello.txt
hello
missing:~$ cat < hello.txt
hello
missing:~$ cat < hello.txt > hello2.txt
missing:~$ cat hello2.txt
hello
- 管道操作符|可以将两个命令分隔开,“|”左边命令的输出就会作为“|”右边命令的输入
|
、>
、和<
是通过 shell 执行的,而不是被各个程序单独执行。所以在使用管道符或者重定向操作符访问系统文件时即使程序前使用了sudo还是会报错。因为只是程序有了根权限,但是操作符另一边还是没有。-
$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*' /sys/class/backlight/thinkpad_screen/brightness $ cd /sys/class/backlight/thinkpad_screen $ sudo echo 3 > brightness //像系统亮度文件写入值,结果shell打开文件时被拒绝。 An error occurred while redirecting file 'brightness' open: Permission denied $ echo 3 | sudo tee brightness //正确做法
练习
-
chmod命令用于改变文件权限。
二、shell脚本
- shell里面可以直接对变量赋值,例如foo=bar
- shell里对脚本(.sh)文件的参数名有特殊规定,如$1表示传入的第一个参数。具体如下。
- 命令替换:$(CMD)执行命令后输出结果将会替换掉$(CMD),例如for file in $(ls)。<(CMD) 会将结果输出到临时文件中并将<(CMD)作为临时文件名。如diff <(ls foo) <(ls bar)比较foo和bar文件的区别。
#!/bin/bash echo "Starting program at $(date)" # date会被替换成日期和时间 echo "Running program $0 with $# arguments with pid $$" for file in "$@"; do grep foobar "$file" > /dev/null 2> /dev/null # 如果模式没有找到,则grep退出状态为 1 # 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息 if [[ $? -ne 0 ]]; then echo "File $file does not have any foobar, adding one" echo "# foobar" >> "$file" fi done
-
使用通配符?和*分别代表一位和全部。例如,对于文件
foo
,foo1
,foo2
,foo10
和bar
,rm foo?
这条命令会删除foo1
和foo2
,而rm foo*
则会删除除了bar
之外的所有文件。 -
花括号{}将会展开。如convert image.{png,jpg}会展开为convert image.png image.jpg
-
ubuntu下man的简略替代tldr命令。
-
查找文件使用find。
-
查找文件里的某段代码使用grep。
尤其注意第三题
#!/usr/bin/env bash
n=$(( RANDOM % 100 ))
if [[ n -eq 42 ]]; then
echo "Something went wrong"
>&2 echo "The error was using magic numbers"
#此处的>&2重定向放在了行首(放在行尾也是一样的),表示将后面的输出重定向到标准错误流。与test.sh脚本中2> out.log相配合
exit 1
fi
echo "Everything went according to plan"
#! /bin/bash
count=1
while true
do
./bug.sh 2> out.log
#此处的2>表示只讲标准错误流重定向到out.log中,与bug.sh中的>&2相配合。
if [[ $? -ne 0 ]]; then
echo "failed after $count times"
cat out.log
break
fi
((count++))
done
三、vim(略过)
四、数据整理(没怎么学懂)
- 管道其实就是一种数据整理,例如
ssh myserver journalctl | grep sshd #ssh myserver是连接远程服务器,把journalctl命令放到服务器上执行。再传输到本地使用grep。 ssh myserver 'journalctl | grep sshd | grep "Disconnected from"' | less #打了引号相当于传过去一个整体命令,在远程处理完成后再传输,可以减小流量使用。
-
正则表达式
-
awk-另一种编辑器
五、命令行环境
- 结束进程:使用ctrl+c发送一个SIGINT信号来中止进程。但有些进程无法停止,可以使用ctrl+\命令,或者使用kill -TERM <PID>。
暂停和后台执行进程:ctrl+Z可以让进程暂停, fg 或 bg 命令恢复暂停的工作。它们分别表示在前台继续或在后台继续。jobs命令会列出当前终端会话中尚未完成的全部任务并可以配合%+任务编号来选取任务。然后在命令后面加上&后缀可以让命令直接在后台执行,但此时若关闭终端,后台进程也会终止。为了防止这种情况,可以使用nohup。
$ sleep 1000
^Z
[1] + 18653 suspended sleep 1000
$ nohup sleep 2000 &
[2] 18745
appending output to nohup.out
$ jobs
[1] + suspended sleep 1000
[2] - running nohup sleep 2000
$ bg %1
[1] - 18653 continued sleep 1000
$ jobs
[1] - running sleep 1000
[2] + running nohup sleep 2000
$ kill -STOP %1
[1] + 18653 suspended (signal) sleep 1000
$ jobs
[1] + suspended (signal) sleep 1000
[2] - running nohup sleep 2000
$ kill -SIGHUP %1
[1] + 18653 hangup sleep 1000
$ jobs
[2] + running nohup sleep 2000
$ kill -SIGHUP %2
$ jobs
[2] + running nohup sleep 2000
$ kill %2
[2] + 18745 terminated nohup sleep 2000
$ jobs
- 终端多路复用(tmux)
- 别名,使用alias命令,如
alias alias_name="command_to_alias arg1 arg2" #=两边没有空格
但是shell不会保存别名,即关闭后就没了。若要持续生效,需要放到shell启动文件中,如.bashrc或者.zshrc。
-
配置文件:是shell、git、tmux程序在启动时就执行的文件,例如别名,环境变量等。且配置文件具有可移植性,使用if语句区分不同类型的设备。
-
远端设备
七、版本控制(git)
关键:每次提交都会把更改的文件全部保存下来,并且产生新的索引。版本回退,就是通过之前提交的索引去找之前的文件。但因为提交时未改变的文件索引不会变,所以保存的文件并不会很多。
八、调试及性能分析
调试部分
- 在有问题的地方添加一些打印语句或者使用日志
- 系统日志可以使用journalctl调出,但是内容过多,一般需要检索。
- 调试器:例如GDB等
性能分析
- 使用计时确定哪块代码执行时间长,但需要区分真实时间(从程序开始到结束流失掉的真实时间,包括其他进程的执行时间以及阻塞消耗的时间(例如等待 I/O或网络)、用户时间(CPU 执行用户代码所花费的时间)和系统时间(CPU 执行系统内核代码所花费的时间)。实际进程消耗的CPU=用户时间+系统时间。
- 不同的语言一般都有自己的CPU性能分析器,甚至可以给出每行代码的执行时间。
- 内存分析工具,可以使用类似Valgrind这样的工具检查内存泄漏问题。
九、元编程——关于学习构建系统、代码测试以及依赖管理
- 构建系统:使用Makefile和make。
- 依赖管理:主要是语义版本号,格式为(主版本号.次版本号.补丁号)。相关规则为如果新的版本没有改变 API,请将补丁号递增;如果您添加了 API 并且该改动是向后兼容的,请将次版本号递增;如果您修改了 API 但是它并不向后兼容,请将主版本号递增。向后兼容的意思是使用前面版本编写的代码可以运行在新版本的库上。使用 Python 3.5 编写的代码在 3.7 上可以运行,但是在 3.4 上可能会不行。因为是向后兼容,次版本号改了,新的就不能在旧的上面运行了。
- 持续集成系统:主要是CI(雨伞术语),指的是那些“当您的代码变动时,自动运行的东西”。比如您需要在代码仓库中添加一个文件,描述当前仓库发生任何修改时,应该如何应对。
十、安全和密码学(跳过)