1.什么是Shell

Shell有两层身份:

  1. Shell 是一个“应用程序”(解析器)
    它是用户和操作系统之间的“翻译官”,接收你输入的命令,然后交给内核执行。
    常见的 Shell 应用程序有:
    | Shell | 常见平台 | 特点 |
    | ————————————————— | —————————————————— | —————————————————————— |
    | bash (Bourne Again SHell) | Linux, macOS(默认) | 最常见,功能强,支持数组、字符串操作 |
    | sh(Bourne Shell) | Unix, Linux | 最早的 Shell,兼容性高,功能略弱 |
    | zsh(Z Shell) | macOS(默认从 Catalina 开始), Linux | 更丰富的交互体验,如自动补全、语法高亮 |
    | csh / tcsh(C Shell) | Unix, BSD | 类似 C 语言语法,偏爱者较少 |
    | fish(Friendly Interactive SHell) | macOS, Linux | 用户友好,现代语法,不兼容 bash 语法 |
    | PowerShell | Windows(默认) | 面向对象,支持 .NET,跨平台(有 Linux 版本) |
    | cmd.exe | Windows | 老式命令行,功能有限,不推荐编写复杂脚本 |

    Linux系统默认的解析器是bash,文本使用bash进行。

  2. Shell 是一种“脚本语言”
      可以把 Shell 命令写进一个文本文件里(脚本)。Shell 程序会 逐行解析并执行这些命令。这就是我们常说的 Shell 脚本,比如 .sh 文件。
      脚本的本质是一个文件,通常是纯文本,里面写的是:按照某种“脚本语言”格式组织起来的指令序列。
      shell脚本是一种脚本语言,我们只需使用任意文本编辑器,按照语法编写相应程序,增加可执行权限,即可在安装shell命令解释器的环境下执行
    脚本的特点:

    • 不需要编译,解释器(如 bash)一行一行地“解释”运行。
    • 启动快,适合做自动化任务。
    • 可以修改后立即运行,不需要重新编译。

2.Shell工作原理

以bash为例

  1. 打开一个终端(Terminal)时,Shell 程序就启动了,它会显示一个提示符( $ )等待你输入命令。
    比如输入:
    1
    ls -l
  2. 解析命令(词法分析 & 语法分析)
    Shell 会把你输入的命令进行解析,也叫做“解释”:

    • 拆分成命令和参数,比如:
      命令:ls
      参数:-l
    • 检查语法有没有错。
    • 判断是不是变量、重定向、管道等操作。
  3. 查找命令的位置
    Shell 会去系统的环境变量中查找这个命令对应的可执行文件。
    例如 ls 实际是 /bin/ls 这个程序,它会被找到并准备执行。

  4. 创建子进程并执行命令
    Shell 并不会自己“执行”命令,它会:
    使用 fork() 创建一个子进程;
    在子进程中调用 exec() 去执行你指定的程序(比如 /bin/ls);
    自己继续等待子进程执行完。

  5. 等待命令执行完成并返回结果
    命令执行完后,会返回一个 退出状态码(Exit Status),告诉 Shell 是否成功。
    如果成功,状态码是 0。
    如果失败,是非零的数字。
    Shell 再把程序输出的内容显示在你的终端上。

  6. 等待下一条命令
    一轮执行完后,Shell 又回到第一步,等你下一次输入。

3.Shell脚本编程

当你执行一个 .sh 脚本时,Shell 会:

  1. 打开脚本文件;
  2. 从上到下一行一行地执行;
  3. 每行执行过程其实跟上面的流程一样。

3.1。Shell脚本执行方式

  1. 直接执行
    1
    ./myscript.sh
    前提是要先赋予权限
    1
    chmod +x myscript.sh
  2. 用解释器运行脚本(不需要可执行权限)
    1
    bash myscript.sh
  3. 通过绝对路径或相对路径调用
    1
    /home/user/scripts/myscript.sh
    或者
    1
    ../scripts/myscript.sh
  4. 把脚本放入 PATH 中,像命令一样调用

    3.2.两个特殊的脚本

  • /etc/profile 是一个系统级的 Bash 启动脚本,用于为所有用户在登录时设置统一的环境变量和初始化操作。用户第一次登录 Bash Shell 时,该文件就会被自动执行。常用于设置公共环境变量、默认路径、语言设置,以及调用系统开机自动执行的脚本(如 /etc/profile.d/*.sh 中的内容)等。
  • ~/.bashrc 是当前用户的 Bash Shell 启动配置文件, 是当前用户每次打开 Bash 终端时自动执行的脚本,用于配置环境变量、别名、自定义命令等
    • 环境变量(比如 PATH)
    • 别名(alias)
    • Shell 提示符(PS1)
    • 自定义函数
    • 运行你想每次打开终端时自动执行的命令
  • 当你真正“登录”进系统(比如 ssh 登录或 tty 登录),系统会运行一次登录型的 Shell,这时 /etc/profile 会自动跑一次。
  • 如果你只是打开一个普通终端窗口,那跑的是非登录型 shell,它加载的是 ~/.bashrc 这类文件,而不是 /etc/profile。
  • 如果你想让所有用户一登录就执行某个命令,就写在 /etc/profile;如果你想自己每次打开终端都有某些设置,就写在 ~/.bashrc。

3.3 Shell脚本编程语法

3.3.1.基础语法

一个最基本的 Shell 脚本长这样:

1
2
3
#!/bin/bash
# 输出Hello world
echo "Hello, world!"

  1. #!/bin/bash:指定脚本解释器,必须写,放在第一行。
  2. #:注释,不会被执行。
  3. echo:输出

    3.3.2 输入输出

    1
    2
    read -p "请输入姓名:" username
    echo "你好,$username"

    3.3.3.变量

    Shell 变量是用来存储数据的命名位置。每个变量都有一个名称(标识符)和一个对应的值。在 Shell 中,变量可以存储字符串、数字或任何有效的数据类型。
  4. 定义和使用
    1
    2
    3
    4
    5
    name="Alice"        #定义变量,使用等号(=)进行赋值,等号两边不能有空格
    echo $name #引用变量,使用变量的值需要在变量名前面加上美元符号($)
    echo "Hi, $name!" #在双引号中引用变量也能被解析
    echo ${name} #使用花括号增强可读性(推荐)
    echo "Hello, ${name}!"
    1. 变量名以字母、数字、下划线组成;不能以数字开头;
    2. 等号两边不能有空格
    3. 推荐用小写变量名,避免和系统变量冲突
  5. 系统变量

    • $HOME 当前用户主目录
    • $USER 当前用户名
    • $PWD 当前工作目录(print working dir)
    • $SHELL 当前使用的 Shell 程序路径
    • $PATH 可执行命令搜索路径
  6. 预设变量

    • $0 当前脚本或命令的名称。
    • $1~$n 第1~n个参数
    • $@ 所有参数(每个独立)
    • $* 所有参数(视为一个)
    • $# 参数个数
      例如:
      alt text
      alt text
  7. 作用域
    Shell 变量的作用域通常是局部的。
    |类型|定义方式|作用范围|
    |—-|—-|—-|
    |局部变量| name=”Alice” |当前 Shell 或函数|
    |环境变量| export name=”Alice” |当前 Shell 和其子进程|
    |函数局部变量| local name=”Alice” |仅在当前函数中|

  1. 变量的默认值写法(防止为空)
    1
    echo "Hello, ${name:-Guest}"   # 如果name为空,就显示 Guest
  2. 删除变量
    1
    unset name

3.3.4. 数组

  1. 数组定义和访问
    1
    2
    3
    4
    MY_ARRAY=(value1 value2 value3)
    echo ${MY_ARRAY[0]} # 输出第一个元素
    echo ${MY_ARRAY[@]} # 输出所有元素
    echo ${MY_ARRAY[*]} # 输出所有元素
  2. 遍历数组
    1
    2
    3
    4
    5
    # 使用 for 循环遍历数组
    for value in "${MY_ARRAY[@]}"
    do
    echo "value: $value"
    done

3.3.5.运算符

  1. 算术运算符
    以使用算术运算符进行数学运算:
    • +:加法
      • -:减法
    • *:乘法
    • /:除法
    • %:取模(求余)
  2. 逻辑运算符
    逻辑判断:
    • -eq:等于
    • -ne:不等于
    • -gt:大于
    • -lt:小于
    • -ge:大于等于
    • -le:小于等于
  3. 字符串运算符
    用于字符串处理的运算符:
    • =:字符串相等
    • !=:字符串不等
    • -z:字符串为空
    • -n:字符串非空

      3.3.6. 语句

  4. if语句
    1
    2
    3
    4
    5
    6
    7
    age=18

    if [ $age -ge 18 ]; then
    echo "成年人"
    else
    echo "未成年人"
    fi
  5. case语句
    1
    2
    3
    4
    5
    6
    7
    8
    read -p "输入成绩等级:" grade

    case $grade in
    A) echo "优秀" ;;
    B) echo "良好" ;;
    C) echo "及格" ;;
    *) echo "不及格" ;;
    esac
  6. for循环
    1
    2
    3
    4
    for i in 1 2 3 4 5
    do
    echo "数字:$i"
    done
  7. while循环
    1
    2
    3
    4
    5
    6
    count=1
    while [ $count -le 5 ]
    do
    echo "计数:$count"
    count=$((count+1))
    done

    3.3.7. 函数

  8. 定义函数

    1
    2
    3
    4
    5
    6
    7
    function_name() {
    # 函数体(命令序列)
    }
    #或者 旧写法
    function function_name {
    # 函数体(命令序列)
    }
  9. 调用

    1
    2
    3
    4
    5
    function my_func {
    echo "Hello from function"
    }

    my_func
  10. 函数传参
    Shell 函数的参数和脚本参数一样,用 $1, $2, $3 表示第 1 个、第 2 个、第 3 个参数…
    1
    2
    3
    4
    5
    6
    7
    greet() {
    echo "Hello, $1! You are $2 years old."
    }

    greet "Alice" 25

    #输出 Hello, Alice! You are 25 years old.
  11. 返回值
    Shell 函数的 return 只能返回 0~255 之间的整数(代表状态码)。

    1
    2
    3
    4
    5
    6
    add() {
    return $(($1 + $2))
    }

    add 3 4
    echo $? # 用 $? 获取函数返回值(是 7)

    如果想返回字符串或其他数据,用 echo + 命令替换 的方式:

    1
    2
    3
    4
    5
    6
    add() {
    echo $(($1 + $2))
    }

    result=$(add 3 4)
    echo "结果是 $result"
  12. 局部变量(防止变量污染)
    1
    2
    3
    4
    my_func() {
    local name="Tom" # 仅在函数内部有效
    echo $name
    }