「Linux」 Shell

Posted by Dawn-K's Blog on February 8, 2021

Shell

[toc]

概述

Shell 是提供给用户的用以访问操作系统服务的程序.

sh(Bourne Shell)是一个早期的重要shell, 1978年由史蒂夫·伯恩编写, 并同Version 7 Unix一起发布.

bash(Bourne-Again Shell)是一个为GNU计划编写的Unix shell.1987年由布莱恩·福克斯创造. 主要目标是与POSIX标准保持一致, 同时兼顾对sh的兼容, 是各种Linux发行版标准配置的Shell, 在Linux系统上/bin/sh往往是指向/bin/bash的符号链接.

简单说, sh是bash的一种特殊的模式, sh就是开启了POSIX标准的bash, /bin/sh 相当于 /bin/bash --posix

Shell 脚本

执行程序

Shell 脚本的第一行常常写为

1
#!/bin/sh

虽然 # 开头的是注释, 但是这个写在第一行就是指定使用哪个解释器来翻译.

变量

在交互模式下逐行输入下面命令

1
2
3
4
5
6
7
8
9
# 定义变量(不能加空格)
foo=bar
$foo
foo = bar # 会将 foo 视作可执行文件,从而报错

# 单引号不会转义,双引号会转义
echo "$foo" # bar
echo '$foo' # $foo

环境变量

shell 内置了一些常用的变量. 可以通过 printenv 来打印出所有的变量. 也可以通过c语言的stdio.h库的 environ变量进行打印.

1
2
3
4
5
6
7
8
9
#include<stdio.h>
extern char** environ;
int main(){
        char ** var;
        for(var = environ;*var!=NULL;++var){
                printf("%s\n",*var);
        }
        return 0;
}

export 命令可以进行环境变量的增删改查, 但是仅限于当前登录活动.

环境变量相当于上文提到的本地变量的区别就是, 本地变量仅能在当前shell中使用, 而不能在当前shell执行的脚本或者子进程中使用. 但是环境变量可以.

$USER # 用户名
$HOME # 个人目录
$PATH # 环境变量的路径
$DISPLAY # X窗口服务器名称和显示器编号

函数

编写文件 mcd.sh

1
2
3
4
5
6
#!/bin/sh
mcd(){
    # $1 就是传入的第一个参数,类似 argv[1]
    mkdir -p "$1"
    cd "$1"
}

然后执行 source mcd.sh , 看似无事发生, 实际上已经将 mcd导入了 shell 中, 在之后就可以直接使用了.

mcd new_folder 会创建 new_folder 文件夹并 cd 进去

特殊变量

$0 - 脚本名 $1$9 - 脚本的参数. $1 是第一个参数, 依此类推. $@ - 所有参数 $# - 参数个数 $? - 前一个命令的返回值(往往0表示正常退出, 如果用c语言的话, 就是main函数或者exit的返回值) $$ - 当前脚本的进程识别码 !! - 完整的上一条命令, 包括参数. 常见应用: 当你因为权限不足执行命令失败时, 可以使用 sudo !! 再尝试一次. $_ - 上一条命令的最后一个参数. 如果你正在使用的是交互式shell, 你可以通过按下 Esc 之后键入 . 来获取这个值.

逻辑操作

有趣的是, 根据上文的 $? , true 的返回值恒为0, false 返回值恒为1. && 链接两个命令, 仅当第一个返回值为0时才执行第二个. 为方便记忆, 即前面成功才执行后面. || 链接两个命令, 仅当第一个返回值为1时才执行第二个. 为方便记忆, 即前面失败才执行后面. ; 也是如此, 不过是无论第一个的返回值如何, 都会执行第二个

1
2
3
4
5
true  && echo "success"
false && echo "This is not print"
false || echo "success"
true  || echo "This is not print"
false ; echo "This will always run"

通配符

*: 匹配任意长度的任意字符.如 foo*可以匹配 foo foo1 foobar

+: 匹配单个任意字符.如 foo*可以匹配 foo1 foo2

{}: 花括号用于自动填充字符串, 如 ls image.{png,jpg},会自动扩展为 ls image.png image.jpg

综合运用

创建 example.sh , 然后赋予执行权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

# 日期 以 $(cmd) 的形式就是将命令执行完的输出替换占位符
echo "Starting program at $(date)" # Date will be substituted

# 文件名 , 参数数量 , 进程号
echo "Running program $0 with $# arguments with pid $$"

# 遍历参数
for file in "$@"; do
    # 让标准输出 和 错误输出都重定向到 null
    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
    # ne 即 not equal ,判断 grep 的返回值,如果没有 foobar,那么在文件末尾加上 #  foobar
    if [[ $? -ne 0 ]]; then
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done

运行结果如下

1
2
3
4
$ ./example.sh mcd.sh example.sh
Starting program at 2021年 02月 08日 星期一 12:15:07 CST
Running program ./example.sh with 2 arguments with pid 8972
File mcd.sh does not have any foobar, adding one

常用工具

fd

fd 是用于替换掉 find的工具. 此工具更快而且更易使用

1
2
# 在 ~ 下递归搜索后缀为 sh 的, 文件名中含 mcd 的文件
fd mcd  -p ~ -e sh

参考资料

MIT 6. NULL B站视频 MIT 6. NULL 英文讲义 MIT 6. NULL 中文讲义 Advanced Linux Programming