MIT-Missing-Semester笔记(一):Shell
2022-12-03

MIT的课程:Missing Semester

介绍了Shell、vim、git等一些常用工具

检查Shell语法错误的在线网站ShellCheck

Shell

介绍

一开始介绍了一些常用的命令,大部分之前使用过。

重定向符号<>,追加用>>

echo hello
cd - #回到上一个目录
cat 2.txt >> 1.txt
ls -l | tail -n3 #输出最后三行
man ls #多用man
ls -ls #-l显示详细信息,-s显示文件占用块数block,后面可跟上目录
4 -rwxrw-r-- 1 hsl hsl  132 123 20:26 test
#文件占用块数、-文件d目录l链接
#权限:rwx读写执行权限,分别是拥有者、用户组、其他人
#文件数量、拥有者、用户组、时间、文件名
chmod u+x test
curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2

\转义字符,mkdir my\ photo将空格转义,否则会建立两个目录

touch命令本意是修改时间戳,-a修改访问时间,-m改变修改时间,文件不存在时会默认新建

.sh文件开头#!/bin/sh是shebang,指示解释器运行环境

/sys目录下有很多系统设置,一切皆文件!

tee命令读取标准输入,写到标准输出和文件

还讲了find、grep、curl、cut命令,后面再看一下

变量、函数

变量定义: foo=bar, 等号左右不能有空格,否则相当于调用foo命令,=和bar作参数。

foo=bar #单引号、双引号不同
echo "$foo"
# prints bar
echo '$foo'
# prints $foo

写一个mcd命令

cat mcd.sh
mcd () {
	mkdir -p $1
	cd $1
}
source mcd.sh
  • $0 - Name of the script
  • $1 to $9 - Arguments to the script. $1 is the first argument and so on.
  • $@ - All the arguments
  • $# - Number of arguments
  • $? - Return code of the previous command
  • $$$$ - Process identification number (PID) for the current script
  • !! - Entire last command, including arguments. A common pattern is to execute a command only for it to fail due to missing permissions; you can quickly re-execute the command with sudo by doing sudo !!
  • $_ - Last argument from the last command. If you are in an interactive shell, you can also quickly get this value by typing Esc followed by . or Alt+.

详细见:https://missing.csail.mit.edu/2020/shell-tools/

替换

command substitution

for file in $(ls)

process substitution

diff <(ls foo) <(ls bar), <( CMD ) 将执行 CMD 并将输出放在一个临时文件中,并将 <() 替换为该文件的名称。

脚本举例

举例(比较时尽量使用双方括号, why

#!/bin/bash

echo "Starting program at $(date)" # Date will be substituted

echo "Running program $0 with $# arguments with pid $$"

for file in "$@"; do
    grep foobar "$file" > /dev/null 2> /dev/null
    # When pattern is not found, grep has exit status 1
    # We redirect STDOUT and STDERR to a null register since we do not care about them
    if [[ $? -ne 0 ]]; then
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done
  • 正则表达式

? 匹配一个任意字符,* 匹配多个,{} 匹配花括号里所有

一个python脚本:

#!/usr/bin/env python3
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

shebang写成这样移植性更好

函数和脚本

  • 函数(平时一般叫命令?)必须和shell用同样的语言,脚本可以用任何语言,所以shebang很重要。
  • 函数在读取其定义时加载一次。每次执行脚本时都会加载脚本。这使得函数的加载速度稍快一些,但是无论何时更改它们,都必须重新加载它们的定义。source命令吧
  • 函数在当前的 shell 环境中执行,而脚本在它们自己的进程中执行。因此,函数可以修改环境变量,例如更改当前目录,而脚本不能。后面再看下export命令

查找

# Find all directories named src
find . -name src -type d
# *匹配0个或多个目录,在文件那匹配0个或多个字符,?只能匹配一个
find . -path '*/test/*.py' -type f
# Find all files modified in the last day
find . -mtime -1
# Find all zip files with size in range 500k to 10M
find . -size +500k -size -10M -name '*.tar.gz'
# Find all python files where I used the requests library
rg -t py 'import requests'
# Find all files (including hidden files) without a shebang line
rg -u --files-without-match "^#!"
# Find all matches of foo and print the following 5 lines
rg foo -A 5
# Print statistics of matches (# of matched lines and files )
rg --stats PATTERN
# -R递归搜索目录,-n显示行号,-C显示邻近几行
grep -R -n -C 5 foobar .

其他类似grep的搜索工具 ack, agrg, 模糊匹配的fzf. 还有两个终端文件管理工具,nnnranger,看着挺炫酷,后面试一试

其他

  • tldr工具

  • 一些命令不能从管道中读取数据,xargs可以将标准输入转化为参数。ls | xargs rm

课后习题

  1. ls -lath --color=auto, -a显示隐藏文件,-t按修改时间降序排列,-h大小显示为多少M,多少k的形式。

  2. 执行marco时记录下当前目录,执行polo时回到之前记录的目录。

marco() {
	export MARCO=$(pwd)
}

polo() {
	cd "$MARCO"
}
  1. 运行err.sh脚本, 记录其输出和错误日志, 输出其出错时的运行次数

err.sh

#!/usr/bin/env bash

 n=$(( RANDOM % 100 ))

 if [[ n -eq 42 ]]; then
    echo "Something went wrong at $(date)"
    # >2是将输出重定向到文件2, &2才是标准错误
    echo "The error was using magic numbers" 1>&2
    exit 1
 fi

 echo "Everything went according to plan"

errTest.sh

#!/usr/bin/env bash
rm record.txt record2.txt
t=1
./err.sh > ./record.txt 2> ./record2.txt
while [[ $? -eq 0 ]]
do
	let t++
	./err.sh >> ./record.txt 2>> ./record2.txt
done
cat record.txt
echo "------"
cat record2.txt
echo "run counts: $t"
  1. 搜索当前文件下的html文件并打包压缩,搜索出来的内容以换行符区分 ( 排除文件名中包含空格的情况 ) .
# xargs -d 指定分隔符
find -name "*.html" | xargs -d '\n' tar -czvf archieve.tar.gz
  1. 递归搜索当前文件夹中的文件,按文件最后修改时间逆序排列
# stat -c或--format格式化输出 Y是UNIX时间戳,y是可读时间,n文件名
# sort -n按数值比较 -r逆序
# cut -d分隔符 -f, --fields显示什么字段,从1开始,2-取第二个及以后
# head列出前几行,默认10
find . -type f | xargs -d '\n' stat --format "%Y %y %n" | sort -nr | cut -d ' ' -f 2- | head -n 5
2022-12-28 20:46:34.869589528 +0800 ./22/33/1 3.html
2022-12-28 20:46:00.144839386 +0800 ./22/2 .html
2022-12-28 20:45:30.952572281 +0800 ./1.html
2022-12-27 23:13:16.676754571 +0800 ./record.txt
2022-12-27 23:13:16.676754571 +0800 ./record2.txt