Skip to content

Shell

一、概念

1. Shell

Shell 是一种用户与操作系统之间的接口,也被称为“命令行解释器”或“命令行界面”(CLI),用于解释用户的命令并将它们传递给操作系统执行。

1.1 Shell 有两种主要类型:

  1. 图形化 Shell:如 Windows 的桌面环境、macOS 的 Finder 等。
  2. 命令行 Shell:通过文本命令与系统交互,如 Bash、Zsh、Cmd 等。

1.2 Shell 的作用

  1. 命令解释:将用户输入的命令(如 ls、cd、mkdir)解释为系统可以理解的操作。
  2. 脚本编写:用来编写脚本文件,自动执行一系列命令和任务。
  3. 环境管理:管理系统环境变量、用户配置,并通过这些变量来控制操作系统的行为。
  4. 任务控制:提供了任务控制(如后台任务运行),并允许用户查看和控制正在运行的进程。

1.3 Shell 的类型

不同的操作系统和平台提供不同类型的命令行 Shell 实现。常见的 Shell 包括:

  • Bash(Bourne Again Shell):Linux 和 macOS 上常见的 Shell,支持复杂的脚本和命令操作。
  • Zsh(Z Shell):Bash 的增强版,提供更多的特性,如更智能的自动补全、插件支持等。
  • Cmd:Windows 上的命令提示符,简单但功能有限,适用于基本的系统管理任务。
  • PowerShell:Windows 上的另一种命令行 Shell,支持对象和更复杂的脚本操作。
  • Fish(Friendly Interactive Shell):另一个现代 Shell,注重易用性和用户友好性。

1.4 与 Linux 的关系

  1. Shell 命令:

    • Shell 是一个命令解释器,负责解释和执行用户输入的命令。Shell 提供了一些内置命令,比如 cd、echo、exit 等,这些命令是由 Shell 自身实现的,不依赖于外部程序。
    • 例如:cd 用于更改目录,它是 Shell 自带的一个内置命令。
  2. Linux 命令:

    • Linux 命令通常是外部程序或工具,由操作系统提供,并存储在系统的某些目录中(例如 /bin, /usr/bin 等)。当用户在 Shell 中输入这些命令时,Shell 会查找这些程序的路径并执行它们。
    • 例如:ls、cp、grep 等命令都是 Linux 系统提供的外部命令,而不是 Shell 自身的内置功能。
  3. 二者的关系:

    • Shell 作为命令解释器,可以执行内置的 Shell 命令和 Linux 系统命令。
    • Linux 系统命令可以看作是操作系统提供的外部工具和程序,它们是 Shell 调用来执行任务的。

2. Shell 脚本

Shell 脚本 是一种用来编写命令行任务自动化的程序脚本,通常由一系列 Shell 命令组成,主要目的是通过自动化的方式执行一系列的系统命令、处理文件、运行程序等任务。

这些脚本可以运行在命令行解释器(如 Bash、Zsh、Cmd 或 PowerShell)之上,脚本文件名可以使用 .sh 扩展名,但这不是必须的,只要系统能识别即可。

2.1 Shell 脚本的用途

  1. 系统管理:自动化执行备份、软件安装、日志管理等任务。
  2. 文件处理:批量重命名文件、移动文件、查找/替换内容等。
  3. 任务调度:结合系统的任务调度工具(如 cron),定期执行维护任务。
  4. 部署脚本:用于自动化应用程序的部署和环境配置。
  5. 自动化测试:自动执行测试用例或软件的构建和部署流程。

2.2 Shell 脚本的基本结构

shell
#!/bin/bash
# 这是一个简单的 Shell 脚本示例

name="Yun"
echo "Hello, $name!"

# 循环示例
for i in {1..3}; do
  echo "Iteration $i"
done

# 条件示例
if [ "$name" == "Yun" ]; then
  echo "Welcome, Yun!"
else
  echo "Who are you?"
fi
  1. Shebang

在脚本文件的第一行用于指定脚本解释器的特殊注释行。
它告诉操作系统该脚本应使用哪个解释器来执行。

shell
# 指定 Bash 解释器 确保系统在执行脚本时使用 Bash 来解析和执行命令。
#!/bin/bash

# 指定 Zsh 解释器 确保系统在执行脚本时使用 Zsh 来解析和执行命令。
#!/bin/zsh

# 使用 env 命令来查找并执行指定的解释器。常用于保证脚本在不同环境中的兼容性。
#!/usr/bin/env bash
  • 使用 #!/bin/bash#!/bin/zsh 适合在你确定脚本将运行在特定环境的情况下。
  • 使用 #!/usr/bin/env 适合在跨平台或多人协作的项目中,以确保脚本能够在不同的系统上无缝运行。
  1. 注释
shell
# 这是一个注释
  1. 变量
shell
variable_name="variable_value"
  1. 命令
shell
command_name
  1. 函数
shell
function_name() {
    command1
    command2
    ...
    commandN
}
  1. 条件判断
shell
if condition
then
    command1
    command2
    ...
    commandN
fi

3. 脚本支持的语言类型

Shell 脚本主要是用 Shell 语言(如 Bash、Zsh、Sh)编写的,但你也可以使用 Python、Perl、Ruby、PHP、Node.js 等语言,只需要在脚本的开头使用适当的 Shebang 来指定解释器。

shell
#!/bin/bash

#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/php
#!/usr/bin/node

二、Shell 命令

1. 变量

1.1 定义变量

shell
# 定义变量
variable_name="variable_value"

# 通过命令赋值
variable_name=$(command)

变量名的命名规则:

  • 变量名和等号之间不能有空格
  • 区分大小写
  • 不能使用特殊字符,如 @#$%^&*()+=
  • 不能使用空格
  • 不能使用数字开头
  • 不能使用下划线开头
  • 不能使用减号开头

1.2 使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

shell
variable_name="variable_value"

# 变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界
echo $variable_name
echo ${variable_name}

1.3 Shell 变量分类

Shell 变量分为两种类型:

  • 局部变量:在当前 Shell 进程中有效的变量,脚本中定义的变量默认就是局部变量。
  • 环境变量:可以从当前 Shell 传递到子进程的变量。通过 export 命令可以将局部变量升级为环境变量。
shell
# 局部变量
variable_name="variable_value"

# 环境变量
export variable_name="variable_value"

1.4 特殊变量

Shell 中有一些预定义的特殊变量,它们通常用于脚本参数传递和脚本控制:

  • $0:当前脚本的文件名。
  • $1, $2, ...:传递给脚本或函数的参数。
  • $#:传递给脚本或函数的参数个数。
  • $@:所有参数的列表,参数以空格分隔。
  • $?上一个命令的退出状态,返回 0 表示成功,非 0 表示失败。(常用)
  • $$:当前 Shell 进程的进程 ID。
  • $!:后台运行的最后一个进程的进程 ID。

2. 注释

# 开头的行都是注释,会被解释器忽略。

shell
# 这是一个注释

sh里没有多行注释,只能每一行加一个#

3. 字符串

字符串可以用单引号,也可以用双引号,也可以不用引号(不推荐)

shell
# 单引号
  # 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
  # 单引号字串中不能出现单引号(对单引号使用转义符后也不行)
str='this is a string'

# 双引号
  # 双引号里可以有变量
  # 双引号里可以出现转义字符
str="this is a string"

3.1 字符串操作

shell
# 获取字符串长度
str="this is a string"
echo ${#str}  # 输出 16

# 提取子字符串
str="this is a string"
echo ${str:0:5}  # 输出 this
echo ${str:7}  # 输出 is a string, 如果省略长度,则会从起始位置提取到字符串的末尾:

# 查找子字符串
str="this is a string"
echo `expr index "$str" is`  # 输出 3  要在linux下运行,mac下会报错

# 字符串拼接
str1="this is "
str2="a string"
echo ${str1}${str2}  # 输出 this is a string

# 字符串替换
str="this is a string is"
# 只替换第一个匹配的子串
echo ${str/is/IS}  # 输出 this IS a string is
# 替换所有匹配的子串
echo ${str//is/IS}  # 输出 this IS a string IS

# 字符串比较
str1="Hello"
str2="World"
# 通过 = 和 != 操作符可以进行相等或不相等的比较
if [ "$str1" = "$str2" ]; then
    echo "字符串相等"
else
    echo "字符串不相等"
fi

# 检查字符串是否为空
# -z 表示字符串为空,-n 表示字符串不为空
str="Hello"
# 检查字符串是否为空
[ -z "$str" ] && echo "字符串为空" || echo "字符串不为空"
[ -n "$str" ] && echo "字符串不为空" || echo "字符串为空"

4. 数组

4.1 普通数组

shell
# 定义数组
# 使用 () 来定义数组,数组的各个元素之间用空格分隔。
array_name=(value1 value2)

# 读取数组
# 使用 ${array_name[index]} 来读取数组中的元素。
echo ${array_name[0]}  # 输出 value1
#  获取数组的所有元素
echo ${array_name[@]}  # 输出 value1 value2

# 修改数组元素
array_name[1]="blueberry"
echo ${array_name[1]}  # 输出:blueberry

# 追加元素
array_name[2]="cherry"
echo ${array_name[2]}  # 输出:cherry

# 获取数组长度   ${#数组名[@]}
echo ${#array_name[@]}  # 输出:2

4.2 关联数组

关联数组是 Shell 4.0 及以上版本引入的一种数据结构,它允许使用字符串作为数组索引。 需要注意,关联数组必须先通过 declare -A 进行声明。

shell
# 声明关联数组
declare -A ass_array
# 给关联数组赋值
ass_array["key1"]="value1"
ass_array["key2"]="value2"

# 读取关联数组
echo ${ass_array["key1"]}  # 输出:value1

# 获取关联数组的所有元素
echo ${ass_array[@]}  # 输出:value1 value2
# 获取关联数组的键
echo ${!ass_array[@]}  # 输出:key1 key2

# 修改关联数组元素
ass_array["key1"]="new_value"
echo ${ass_array["key1"]}  # 输出:new_value

# 获取关联数组的长度
echo ${#ass_array[@]}  # 输出:2

# 删除关联数组元素
unset ass_array["key1"]
echo ${#ass_array[@]}  # 输出:1
# 删除整个数组
unset ass_array
echo ${#ass_array[@]}  # 输出:0

4.3 数组遍历

shell
# 遍历普通数组
for value in ${array_name[@]}; do
    echo $value
done

# 遍历关联数组: 与普通数组类似,但使用 ${!数组名[@]} 来遍历键。
for key in ${!ass_array[@]}; do
    echo "$key => ${ass_array[$key]}"
done

5. 管道

管道是 Shell 中用于将一个命令的输出作为另一个命令的输入的一种机制。
通过使用管道,可以将多个简单的命令组合起来执行复杂的任务。管道符号是竖线符号 |

shell
# 使用 `ls` 列出当前目录中的所有文件,并通过 `grep` 过滤出包含 "txt" 的文件
ls | grep "txt"

# 多重管道
# 先使用 `ps` 列出所有进程,再通过 `grep` 过滤出包含 "bash" 的进程,最后使用 `wc -l` 计算结果行数
ps aux | grep "bash" | wc -l

# 管道与重定向结合
# 将 `ls` 命令的输出通过管道传递给 `grep` 命令,同时将结果写入到 `output.txt` 文件中
ls | grep "txt" > output.txt

# 管道与错误处理
# 管道默认只传递标准输出,而不会传递标准错误(错误消息)。
# 如果需要将错误消息也通过管道传递,可以使用 2>&1 将标准错误重定向到标准输出。

# 将 `ls` 的标准错误(如果有)也传递给 `grep`
ls invalid_directory 2>&1 | grep "No such"

6. 重定向

将命令的输出保存到文件中,或者将文件的内容作为命令的输入,还可以处理错误输出。

Shell 中,每个进程默认有三个与终端交互的文件描述符:

  • 标准输入(stdin):文件描述符 0,通常从键盘输入。
  • 标准输出(stdout):文件描述符 1,通常输出到终端。
  • 标准错误(stderr):文件描述符 2,用于输出错误消息,默认输出到终端。

6.1 重定向输出

shell
# 将 `ls` 命令的输出写入到 `output.txt` 文件中
ls > output.txt

# 追加标准输出到文件
ls >> output.txt # 使用 >> 可以将输出追加到文件末尾,而不是覆盖原有内容。

6.2 输入重定向

使用 < 将文件内容作为命令的标准输入。

shell
# 使用 file.txt 作为标准输入,传递给 `grep` 进行处理
grep "pattern" < file.txt

6.3 错误重定向

标准错误输出(stderr)可以使用 2> 重定向到文件中。类似地,2>> 会将错误消息追加到文件中。

shell
# 重定向错误输出到文件:
# 将 `ls` 命令的错误信息保存到 error.log 文件中
ls invalid_dir 2> error.log

# 追加错误输出到文件
ls invalid_dir 2>> error.log

6.4 合并标准输出和错误输出

可以用 2>&1 来将标准错误重定向到标准输出,然后再将标准输出重定向到文件。

shell
# 将 `ls` 命令的标准输出和错误输出都写入到 `output.txt` 文件中
ls invalid_dir > output.txt 2>&1

6.5 /dev/null

/dev/null 是一个特殊的文件,任何重定向到它的内容都会被丢弃。常用于忽略命令的输出或错误信息。

shell
# 忽略命令的标准输出
ls > /dev/null

# 忽略命令的标准错误
ls invalid_dir 2> /dev/null

# 忽略命令的所有输出(包括标准输出和标准错误)
ls invalid_dir > /dev/null 2>&1

7. 条件判断

7.1 条件判断语句

  • if 语句基本格式
shell
if [ 条件 ]; then  # 条件:用 [] 或 test 表示,可以是数值、字符串或文件判断。
    命令1
    命令2
else
    命令3
fi
  • 文件判断

    可以用来检查文件是否存在、是否是目录、是否可读写等。

    判断条件说明
    -e 文件名判断文件是否存在
    -f 文件名判断是否为普通文件
    -d 文件名判断是否为目录
    -r 文件名判断文件是否可读
    -w 文件名判断文件是否可写
    -x 文件名判断文件是否可执行
shell
if [ -e $file ]; then
    echo "文件存在"
fi
  • 字符串判断

    可以用来比较两个字符串是否相等、是否包含子字符串等。

    判断条件说明
    -z 字符串判断字符串是否为空(长度为 0)
    -n 字符串判断字符串是否不为空
    字符串 1 = 字符串 2判断两个字符串是否相等
    字符串 1 != 字符串 2判断两个字符串是否不相等
  • 数值判断

    可以对数值进行比较,包括等于、大于、小于等。

    判断条件说明
    n1 -eq n2判断两个数是否相等
    n1 -ne n2判断两个数是否不相等
    n1 -gt n2判断 n1 是否大于 n2
    n1 -lt n2判断 n1 是否小于 n2
    n1 -ge n2判断 n1 是否大于等于 n2
    n1 -le n2判断 n1 是否小于等于 n2
shell
if [ $n1 -eq $n2 ]; then
    echo "两个数相等"
fi
  • 复合条件判断

    可以使用 -a、-o 或逻辑运算符 &&、|| 来组合多个条件判断。

shell
# 使用 -a(与)和 -o(或)
if [ $num -gt 5 -a $num -lt 15 ]; then
    echo "num 在 5 和 15 之间"
fi

# 使用 && 和 ||
if [ $num -gt 5 ] && [ $num -lt 15 ]; then
    echo "num 在 5 和 15 之间"
fi
  • -a 和 -o:适用于 test 和 [] 中的条件判断
  • && 和 ||:适用于整个 Shell 命令的逻辑控制

8. 流程控制

8.1 if 语句

shell
if condition
then
    command1
    command2
    ...
    commandN
fi

写成一行(适用于终端命令提示符):

shell
if `ps -ef | grep ssh`;  then echo hello; fi

8.2 if else 语句

shell
if condition
then
    command1
    command2
    ...
    commandN
else
    command
fi

8.3 for 语句

shell
for var in item1 item2 item3 item4 item5
do
    command1
    command2
    ...
    commandN
done

8.4 while 语句

shell
while condition
do
    command
done

8.5 case 语句

shell
case 变量 in
    值1)
        command1
        command2
        ...
        commandN
        ;;
esac

# 举例
case $var in
    "value1")
        echo "Matched value1"
        ;;
esac

9. 函数

9.1 函数定义

shell
# 形式1:
function 函数名 {
    命令1
    命令2
    ...
}

# 形式2:
函数名() {
    命令1
    命令2
    ...
}

9.2 函数调用

通过函数名来调用

shell
# 定义函数
greet() {
    echo "Hello, $1!"
}

# 调用函数并传递参数
greet "Alice" # 输出:Hello, Alice!

9.3 函数参数

函数可以像脚本一样接收参数。函数内部可以通过特殊变量 $1, $2, … 来访问参数:

  • $1, $2, …:表示第 1、2、… 个参数。
  • $#:表示参数的个数。
  • $@:表示所有参数,参数之间保持独立。
  • $*:表示所有参数,参数之间会被合并成一个字符串。
shell
function add() {
    echo $(($1 + $2))
}

add 1 2 # 输出:3

9.3 函数返回值

  • 使用 return 返回状态码
shell
# 函数可以使用 return 语句返回一个状态码(只能是整数)。
# return 的值可以通过 $? 来获取。
# 注意,return 返回的不是函数结果,而是状态码,通常用于表示函数执行成功或失败。
function add() {
    return $(($1 + $2))
}
  • 使用 echo 返回字符串 echo 输出结果,并通过命令替换$(...) 或反引号 ... 来获取返回值。
shell
multiply() {
    echo $(( $1 * $2 ))
}

# 获取函数的输出
result=$(multiply 5 4)
echo "5 * 4 = $result" # 输出:5 * 4 = 20

9.4 函数变量

函数中的变量默认是全局的,这意味着它们可以在函数外部访问。
如果希望函数内部的变量不影响外部,可以使用 local 关键字来定义局部变量。

shell
my_function() {
    local local_var="I'm local"
    global_var="I'm global"
    echo "$local_var"
}

my_function
echo "$global_var"  # 函数外部可以访问
echo "$local_var"   # 函数外部无法访问

# 输出:
# I'm local
# I'm global

10. 接收用户输入

10.1 read 命令

read 命令用于从标准输入读取一行,并将其存储到变量中。

shell
#!/bin/bash
echo "请输入您的名字:"
read name
echo "你好, $name!"

# 带提示信息
read -p "请输入您的名字:" name
echo "你好, $name!"

# 读取多个输入
read -p "请输入您的名字和年龄:" name age
echo "您的名字是 $name,年龄是 $age"

# 输入密码: -s 选项来隐藏用户输入
read -p "请输入您的密码:" -s password
echo "您的密码是 $password"

#  -t 选项设置 read 命令的超时时间
read -t 5 -p "请在5秒内输入您的名字:" name
echo "您的名字是 $name"

10.2 通过 stdin 输入

可以使用 < 来将文件作为标准输入传递给脚本。

shell
#!/bin/bash
read line
echo "你输入的内容是:$line"

# 运行时
./script.sh < input.txt  # 这将读取 input.txt 文件的第一行作为输入。

使用 read 命令时,它默认只读取一行输入。因此,当运行 ./script.sh < input.txt 时,read 命令确实只会读取 input.txt 文件的第一行内容。

10.3 通过 cat 或管道输入

shell
#!/bin/bash
cat input.txt | ./script.sh

其中,input.txt 的内容会通过管道传递给脚本。

11. 脚本执行

11.1 直接执行脚本

shell
# 赋予可执行权限
chmod +x script.sh

# 相对路径或绝对路径执行脚本
./script.sh
/path/to/script.sh

11.2 使用解释器运行脚本

即使脚本没有执行权限,也可以通过显式调用解释器来执行脚本。常用的解释器有 /bin/bash, /bin/sh, /bin/zsh 等。

shell
bash script.sh
zsh script.sh
sh script.sh

11.3 使用 source 或 . 命令执行

当你希望脚本中的变量和函数影响当前 Shell 会话(而不是启动一个新的子 Shell),可以使用 source 或 . 来运行脚本。
这种方式不会启动一个新的进程,而是将脚本中的内容加载到当前 Shell 环境中。

shell
source script.sh
. script.sh

11.4 在后台运行脚本

如果想让脚本在后台运行,可以在执行脚本时添加 & 符号。

shell
./script.sh &

11.5 通过环境变量 PATH 运行脚本

如果想要在任意目录下都能执行某个脚本,可以将脚本所在目录添加到系统的 PATH 环境变量中。
这样就不需要指定脚本的路径,直接通过脚本名运行即可。

shell
# .zshrc 或 .bashrc 中添加
export PATH=$PATH:/path/to/script/directory

11.6 使用 nohup 保持脚本运行

有时你希望在关闭终端后脚本依然继续运行,可以使用 nohup 命令。nohup 会忽略挂起信号,从而保证脚本继续执行。

shell
nohup ./script.sh &

这将使脚本即使在关闭终端后也能继续运行。执行结果会写入默认的 nohup.out 文件中

11.7 定时执行脚本

可以使用 crontab 命令来定时执行脚本。

shell
crontab -e

# 添加定时任务
# 分 时 日 月 周
0 12 * * * /path/to/script.sh

这种方式非常适合自动化脚本任务,例如每天备份数据、定时清理文件等。

shell
# 查看定时任务
crontab -l

# 删除定时任务
crontab -r

12. 常用的命令

12.1 文件与目录操作

命令说明
ls列出目录内容
cd切换目录
pwd显示当前目录
mkdir创建目录
touch创建文件
rm删除文件
rm -r删除目录
rm -rf强制删除目录及其内容
cp复制文件或目录
mv移动文件或目录

12.2 文件查看与编辑

命令说明
head显示文件开头内容
cat显示文件内容
more分页显示文件内容
less分页显示文件内容
tail显示文件结尾内容
grep搜索文件内容
wc统计文件内容
nano文本编辑器
vim文本编辑器
vi文本编辑器

12.3 文件权限

命令说明
chmod修改文件权限
chmod +x file赋予文件可执行权限
chmod 755 file设置文件的权限为 rwxr-xr-x
chown修改文件所有者
chgrp修改文件所属组

12.4 系统信息与监控

命令说明
ps显示当前终端的进程
ps aux显示所有进程
top实时进程
df -h以人类可读的格式显示磁盘空间使用情况
du -sh dir_or_file显示目录或文件的大小
free -m显示内存使用情况
uname -a显示操作系统的详细信息
uptime显示系统运行时间、当前用户数和负载平均值
whoami显示当前用户

12.5 网络操作

命令说明
curl下载文件
wget下载文件
ping测试网络连接
ifconfig显示网络接口配置
netstat显示网络状态
netstat -tuln显示监听的端口

12.6 搜索与过滤命令

命令说明
grep "search_term" file在文件中搜索指定的字符串
grep -r "search_term" dir递归搜索目录中的文件
find /path -name "filename"根据文件名查找文件
find /path -type d -name "dir_name"查找目录
find /path -type f -name "file_name"查找文件
awk '{print $1}' file打印文件的第一列
wc -l file统计文件的行数

12.7 压缩与解压命令

命令说明
tar -czvf archive.tar.gz file_or_directory创建 gzip 压缩包
tar -xzvf archive.tar.gz解压 gzip 压缩包
zip archive.zip file_or_directory创建 zip 文件
unzip archive.zip解压 zip 文件

12.8 其他常用命令

命令说明
echo输出字符串
date显示当前日期和时间
cal显示日历
history显示之前执行的命令

三、脚本兼容性

3.1 bash 和 zsh 的差异语法

Zsh 和 Bash 之间存在一些语法上的不兼容性。这是因为尽管它们都属于 Unix Shell 的家族,并且在设计上有许多相似之处,但它们各自有独特的特性和语法扩展

3.1.1 数组的声明和用法

shell
# bash
array=(one two three)
echo ${array[1]}  # 输出: two


# zsh
array=(one two three)
echo $array[1]    # 输出: one (Zsh 数组是从 1 开始索引的)

3.1.2 条件判断:

shell
# bash
if [ "$var" = "value" ]; then
    echo "Matched"
fi

# zsh
if [[ "$var" = "value" ]]; then
    echo "Matched"
fi

3.2 如何尽可能保证脚本兼容性

  • 遵循 POSIX 标准,避免使用特定于某个 Shell(如 Bash 或 Zsh)的功能或语法
  • 选择合适的 Shebang
  • 在不同的 Shell 和操作系统(如 macOS、Linux、Windows)中运行脚本进行测试。
  • 使用基本命令,确保这些命令在大多数 Unix 和 Windows 环境中都可用。
  • 在脚本中使用条件语句来处理不同环境的差异,例如:
shell
if [ "$(uname)" = "Darwin" ]; then
    # macOS 特定代码
elif [ "$(expr substr $(uname -s) 1 5)" = "Linux" ]; then
    # Linux 特定代码
else
    # 其他系统处理
fi
  • 使用工具如 ShellCheck 来检查脚本的语法和兼容性,识别潜在问题。

君子慎独