本文大部分内容为MIT的《The Missing Semester of Your CS Education》课程的lecture 1和lecture 2的翻译整理
shell是什么?
计算机接收命令的文本接口,可以通过它运行程序,给程序输入,检查程序输出。
怎么用Shell?
下面主要介绍bash(Bourne Again SHell)
shell命令
Shell在环境变量$PATH指定的目录下搜索待运行的程序($PATH里的目录通过:号隔开,可以通过which
查看程序的文件,如which echo
得到/bin/echo),也可以直接指定待运行的程序的路径。
>
输出重定向 <
输入重定向 >>
追加到文件
sudo echo 3 > 系统文件
会报错
An error occurred while redirecting file ''
open: Permission denied
原因:>
、<
和|
操作是由shell完成的,而不是前面的echo程序,echo并不知道这些操作的存在。shell(用户权限)在将文件设置为sduo echo
的输出前,会尝试打开该文件。因为shell不是作为root运行的,所以会报错。
正确的写法
echo 3 | sudo tee 系统文件
shell脚本
source、sh、bash、./都可以执行shell脚本,区别是?
source a.sh
或者. a.sh
在当前shell内读取、执行脚本,而该脚本不需要有“执行权限”
sh a.sh
或者bash a.sh
都是打开一个subshell去读取、执行脚本,同样该脚本不需要“执行权限”。通常在subshell运行的脚本里设置变量,不会影响到父shell。
./a.sh
在当前shell执行脚本,即将a.sh当成一个文件执行,此时需要文件的运行权限。
语法
给变量赋值foo=bar
,之后用$foo
获取这个变量的值。而foo = bar
会被解释为foo程序,参数为=和bar。一般,空格都会被解释为参数分隔符。
函数
mcd () {
mkdir -p "$1"
cd "$1"
}
-
$0
脚本名称 -
$1
到$9
第一到第九个参数 -
$@
所有参数 -
$#
参数个数 -
$?
上一个命令的返回码 -
$$
当前脚本的进程号 -
!!
整个最后一个命令,包括参数。一种常见的用法是仅执行命令以使其由于缺少权限而失败,然后通过sudo !!
快速执行它。也可以直接在交互式shell里这么用。 -
$_
上一条命令的参数。在交互式shell里可以先按Esc再按 . 同样得到上一条命令的参数。
同一行两个命令用;
隔开
命令替换:$( CMD )
执行CMD,得到该命令的输出,并替换$()
。例如for file in $(ls)
进程替换:<( CMD )
执行CMD,将输出保存在一个临时文件中,然后用该文件的名字替换<()
。当命令期望通过文件而不是STDIN传递值时很有用。例如diff <(ls foo) <(ls bar)
将比较foo和bar两个文件夹的里的文件的差异。
举个例子
#!/bin/bash #这行称作Shebang(或Hashbang)
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
# 当文件中不包含foobar,grep的退出状态为1
# 将STDOUT和STDERR重定向到null,因为这里并不关心这两。其中2> 代表重定向STDERR,2和>中间不能有空格,2是文件描述符。
if [[ $? -ne 0 ]]; then
# -ne 不等于,类似的还有 -eq 等于; -ge 大于等于; -gt 大于; -le 小于等于; -lt 小于
# 直接用>或者<,则比较的是字符串
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
if条件,检查文件类型和比较值的语法可以见test
[ 和 [[ 的区别
[ 是命令,和test
等价,即test exper
和[ expr ]
等价
[[ 是关键字,功能更强大,定义了特殊的原语 [[ 有,但 [ 可能缺乏(取决于实现),例如-ef
两个文件是否相等, 更多见http://mywiki.wooledge.org/BashFAQ/031。目前只有bash,Zsh和Korn支持[[]]
通配符
?
匹配一个字符
*
匹配任意个字符
{}
只要在一系列命令中有一个公共子字符串,就可以用{}进行扩展。如:
convert image.{png, jpg}
等价于convert image.png image.jpg
`cp /path/to/project/{foo, bar, baz}.sh /newpath``
``mv *{.py, .sh} folder`
touch {foo, bar}/{a..h}
这个会创建 foo/a, foo/b, ... , foo/h等文件
常用命令
pwd
当前路径man
显示一个程序的手册页,按q退出。如man ls
tee
从输入中读取数据到输出中。如tee file1 file2
将输入的数据同时保存在两个文件中。-a
追加。-
cat
用于连接文件并打印到标准输出设备上。例如
cat -n file1 > file2
把file1的内容加上行号后输入到file2中cat /dev/null > test.txt
清空test.txt touch
用于修改文件或目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。-
chmod[ugoa...][[+-=][rwxX]...][,....]
u表示文件拥有者,g表示与该文件的拥有者同属一个group的用户,o表示其他,a表示这三者都是
+表示增加权限,-表示取消权限,=表示唯一设置权限
X表示只有当该文件是个子目录或者该文件已经被设定过可执行
stat
用于显示inode内容-
awk
是一种处理文本文件的语言,是一个强大的文本分析工具。例如获取文件的最后修改时间
ll file | awk '{print $6 "-" $7 "-" $8}'
-
grep [-r][范本样式][文件或目录...]
用于查找文件里符合条件的字符串若不指定任何文件名称,或是文件名为
-
,则从标准输入读取数据。-r 以递归的方式查找符合条件的文本
例如
grep test *file
在当前目录中的后缀为file的文件中查找包含test字符串的文件,并打印出该字符串的行。
Tips
- 多个单词作为一个参数,除了用单引号或者双引号,如”Hello World“,也可以用\, 如 Hello\ world
- 单引号和双引号的区别 单引号将剥夺其中的所有字符的特殊含义,而双引号中的
$
(参数替换)和'
(命令替换)是例外 - 要进入一个目录,用户必须对该目录及其父目录具有搜索权限(execute,x)
- 基本上/bin下的所有文件对于其他用户都是可执行的
- sysfs 挂载在/sys 下,将许多内核参数以文件的形式暴露出来。例如:屏幕亮度参数在/sys/class/backlight/brightness,通过向该文件写入一个数字,可以改变屏幕亮度
- 工具如https://github.com/koalaman/shellcheck可以帮助检测shell脚本的错误