Notes for Missing Semester

 

shell

用powerShell有些也行

basics

basic commands

在执行程序时在后面使用 -h--help 标记看提示; 在前面使用man查看手册;history打印历史命令

  • date 打印日期、时间
  • echo [参数1] 打印[参数1]; 注意shell中用空格分隔的被认为是不同参数, 例如echo [参数1] [参数2]; 如果需要传递的参数包括空格, 用转义符号\ ; echo 实际上是一个程序,可以直接用路径替代echo来执行
  • $PATH 环境变量, 是用:分隔的地址们
  • which 确定程序名(e.g. echo)代表的实际程序地址

shell 中的路径是一组被分割的目录. 在linux中绝对路径由/分割, 相对路径由\分割. linux中, 路径开头的/ 代表的是系统的根目录,所有的文件夹都包括在这个路径之下, e.g. /bin/echo. Windows中路径用\分割, 这是因为在Windows上每个盘都有一个根目录(例如:C:\)

  • pwd 当前工作目录
  • ls list目录下的所有文件(默认当前目录); ls -l [目录] ;ls *.sh globbing所有.sh后缀名文件;ls [文件名]?仅列举名字多一位的文件(?占一位);ls [文件名]+tab+tab可以筛选出包含这个名字的文件

    文件的属性: drwxr-xr-x d表示目录; 此后三位一读, 依次为文件所有者,用户组与所有人的权限; r = read, w = write, x = execute powerShell中mode有d - Directory, a - Archive, r - Read-only, h - Hidden, s - System, l - Reparse point, symlink, etc

  • cat 读出文件内容
  • mv move
  • rm [文件] remove
  • cp [源] [末] copy
  • mkdir make directory
  • find 递归查找文件
    # 查找所有名称为src的文件夹
    find . -name src -type d
    # 查找所有文件夹路径中包含test的python文件
    find . -path '*/test/*.py' -type f
    # 查找前一天修改的所有文件
    find . -mtime -1
    # 查找所有大小在500k至10M的tar.gz文件
    find . -size +500k -size -10M -name '*.tar.gz'
    # 删除全部扩展名为.tmp 的文件
    find . -name '*.tmp' -exec rm {} \;
    # 查找全部的 PNG 文件并将其转换为 JPG
    find . -name '*.png' -exec convert {} {}.jpg \;
    
  • grep 查找文件内容
    grep "^\(gcc\|g++\)" # 只保留gcc或g++开头的行(正则表达式!)
    

input and output flow

默认的输入输出流是终端

  • [命令] < [输入地址] [命令] > [输出地址] 最简单的重定向到文件(直接替换原有内容), e.g. cat < hello.txt > hello2.txt
  • [命令2] >> [输出地址] (在原来的基础上)追加内容
  • [程序1]|[程序2]|[程序3] 管道,像这样连接多个程序

root user

sudo 以superuser的身份执行 如果向/sys下的系统文件写入,必须作为根用户

$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
#/sys/class/backlight/thinkpad_screen/brightness
$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
#An error occurred while redirecting file 'brightness' open: Permission denied
$ echo 3 | sudo tee brightness
#correct 现在sudo是用于写入了

grammar

  • foo=bar 定义变量,注意没有空格(空格会被识别成参数)!echo "$foo"echo $foo得打印变量中的内容(转义),echo '$foo'打印原义字符串(无转义)
  • foo=$(pwd)命令替换(command substitution).这里pwd命令的输出($(pwd))被写入foo变量,通过STDIN传递返回值
  • diff <(ls foo) <(ls bar)进程替换(process substitution).<(ls foo)将结果输出到一个临时文件,通过文件传递返回值
  • 善用一些常用的参数
    mcd () { # make a directory and move to it
        mkdir -p "$1"
        cd "$1"
    }
    

    函数.source mcd.sh加载并执行函数

    $0 脚本名;$1$9 即其[参数1]至[参数9] $@ 所有参数;$# 参数个数 $? 前一个命令的返回值,e.g.执行完上一个命令后,echo $?STDERR可以输出error code(0 for true, 1 for false) $$ 当前脚本的进程识别码 !! 完整的上一条命令,包括参数,e.g. 权限不足执行命令失败时,可以使用 sudo !!再尝试一次。 $_ 上一条命令的最后一个参数.如果你正在使用的是交互式 shell,你可以通过按下 Esc 之后键入.来获取这个值。

  • 短路运算符(short-circuiting).与退出码搭配使用,e.g.
    false || echo "Oops, fail"
    true || echo "Will not be printed"
    true && echo "Things went well"
    false && echo "Will not be printed"
    false ; echo "This will always run"
    

    || &&代码按顺序从前往后执行,直至满足逻辑(如果第一个已经确定逻辑,之后的不会执行); ;代码并列(均会)执行

  • 这段脚本遍历参数,使用grep搜索字符串foobar,如果没有找到,则将其作为注释追加到文件中。
    #!/bin/bash
    
    echo "Starting program at $(date)" 
    echo "Running program $0 with $# arguments with pid $$"
    for file in "$@"; do
        grep foobar "$file" > /dev/null 2> /dev/null
        # 如果模式没有找到,则grep退出状态为 1
        # STDOUT and STDERR redirected to NULL are discarded
        if [[ $? -ne 0 ]]; then #-ne: not equal (\approx !=)
            echo "File $file does not have any foobar, adding one"
            echo "# foobar" >> "$file"
        fi
    done
    
  • 通配(globbing).分别使用 ?* 来匹配一个或任意个字符,e.g. rm foo* removes files that start with foo.花括号{},自动展开一系列子串
       convert image.{png,jpg}
       # 会展开为
       convert image.png image.jpg
    
       cp /path/to/project/{foo,bar,baz}.sh /newpath
       # 会展开为
       cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath
    
       mv *{.py,.sh} folder
       # 会移动所有 *.py 和 *.sh 文件
    
       mkdir foo bar
       # 排列组合,创建foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h这些文件
       touch {foo,bar}/{a..h}
    
  • #!/usr/local/bin/python#!/usr/bin/env pythonshebang行告诉内核在shell中也用python解释器.import sys是与shell合调所需要的库

    一些工具:shelldeck可以详细解释错误;tldr给出例程

command line environment

  • 结束进程
    • ^c shell 会发送一个SIGINT信号到进程
    • ^\ 发送SIGQUIT信号
    • kill -TERM <PID> 发送更好用的SIGTERM信号
    • 关闭终端发送SIGHUP信号
    • SIGKILL信号不能被进程捕获并且会马上结束该进程,但会有副作用
  • 暂停进程
    • ^z 发送SIGTSTP信号(terminal stop)
    • fg 前台继续
    • bg 后台继续
    • jobs 列出当前终端会话中尚未完成的全部任务.可以使用 pid 引用任务,使用百分号 + 任务编号选取任务,使用 $! 选择最近的一个任务
    • e.g. nohup sleep 2000 & 防止SIGHUP终止后台(&后缀)进程;对已经运行的程序可以使用disown
  • 终端多路复用

    Action Keys combinations
    start tmux tmux
    quit(detach) a session d
    attach and detach session tmux attach/detach, tmux attach/detach -t [name]
    list sessions tmux ls
    create a session tmux new -s [name]
    to travel among sessions s
    kill a session tmux kill-session -t [name]
    Create window c
    Rename window ,
    Switch to previous window p
    Switch to next window n
    Obtain navigable windows list w
    Kill a window &
    Split vertically %
    Split horizontally "
    visualise pane numbers q
    Move pane to the left {
    Move pane to the right }
    Switch panes layout spacebar
    navigating ↑ ↓ ← →
    kill a pane x

git

repository

在一切之前, SSH key配置: https://git-scm.com/downloads

  • 在项目目录下执行git init以创建子目录.git,e.g.
        git add *.c
        git add LICENSE
        git commit -m 'initial version'
    
  • 或者git clone [网址] [自定义名]
  • .gitignore文件中#为注释,用globbing
       # 忽略所有的 .a 文件
       *.a
       # 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
       !lib.a
       # 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
       /TODO
       # 忽略任何目录下名为 build 的文件夹
       build/
       # 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
       doc/*.txt
       # 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
       doc/**/*.pdf
    

status

  • git status,git status --shortgit status -s给出更简洁
  • new file untracked: add->staged;staged:commit->unmodified(up-to-date);
  • existing unmodified: edit->modified,rm->untracked; modified: stage->staged;commit->unmodified
  • 如果add后又修改了? 需要再addstage&commit,不然保存的是老版本
  • it commit -a以跳过staged直接全部提交tracked
  • git rm -f强制删除;git rm --cached [文件名]仅停止跟踪不删除原文件;git rm [目录]/\*.[后缀名],git rm \*[名字结尾]进行批量删除
  • git mv file_from file_to move可以用来重命名
  • git diff给出gnu diff format,对比当前文件与暂存快照的差异;git diff --stage对比暂存文件与提交文件;git diff --cached看已暂存的变化
  • git log --author='author' --since=2.weeks,git log --pretty=format:"%h %s" --graph看提交历史
  • 替代第一次提交
      $ git commit -m 'initial commit'
      $ git add forgotten_file
      $ git commit --amend
    

    取消暂存,建议小心点用git reset HEAD [已暂存文件];撤销修改git checkout [修改后文件]

  • tag

branches

每一个type object = blob | tree | commit,其中blob保存文件快照,tree记录目录,commit包含一个指向上次提交对象(父对象)的指针. Git 的分支本质上是指向提交对象的可变指针.默认分支master指向最后一次提交,特殊指针HEAD指向当前分支,分支独立不干扰,注意用git checkout [分支]切换.

  # 新建并切换分支
  $ git checkout -b hotfix

  # 将hotfix分支合并到master,指针右移(fast-forward)
  # <--hotfix<--master
  $ git checkout master
  $ git merge hotfix
  # 删除(抽离)hotfix
  git branch -d hotfix

如果合并有文件冲突,可以用git status看,用git mergetool解决

fugitive vim extension for git and tag managing