1 shell 简介

命令行界面(CLI, Command Line Interface)是用户与操作系统交互的一种方式,通过输入文本命令来执行操作。不同的操作系统和环境提供了多种命令行解释器(shell),其中 shbash 是最常见和广泛使用的两种。

1.1 sh (Bourne Shell)

  • 历史sh 是最早的 Unix shell 之一,由 Stephen Bourne 在 1977 年为贝尔实验室的第七版 Unix 系统开发。
  • 标准sh 是 POSIX 标准定义的基础 shell,这意味着它提供了一套基本的、跨平台的功能集。
  • 特点
    • 简单且轻量级。
    • 语法相对严格,支持基本的变量、条件判断、循环等。
    • 适合编写简单的脚本或在资源受限的环境中使用。
  • 兼容性:大多数 Unix 和类 Unix 系统都包含一个符合 POSIX 标准的 sh 实现,例如 FreeBSD 的 /bin/sh 或 Debian/Ubuntu 的 dash

1.2 bash (Bourne Again Shell)

  • 历史bash 由 Brian Fox 于 1989 年为 GNU 项目开发,旨在替代 Bourne Shell。
  • 扩展功能bashsh 的超集,提供了许多额外的功能,包括但不限于:
    • 命令行编辑。
    • 命令历史记录。
    • 作业控制。
    • 数组支持。
    • 更强大的字符串处理能力。
    • 高级模式匹配。
    • 动态加载模块。
  • 兼容性bash 可以运行大多数 sh 脚本,并且通常默认安装在大多数 Linux 发行版中。它也是 macOS 和许多其他系统中的默认 shell。
  • 特点
    • 更多的内置命令。
    • 支持函数和局部变量。
    • 更复杂的条件表达式和流程控制结构。
    • 提供了更多的用户自定义选项,如别名、环境变量等。
  • 使用场景bash 适合用于日常的命令行操作和编写复杂的脚本。由于其丰富的功能和广泛的可用性,bash 成为了许多开发者和系统管理员的首选 shell。

1.3 其他常见的 CLI

除了 shbash,还有其他一些常用的命令行解释器:

  • zsh (Z Shell):一种高度可配置的 shell,提供了比 bash 更多的特性,如主题、插件支持等。macOS Catalina 及更高版本将 zsh 作为默认 shell。
  • ksh (KornShell):由 David Korn 开发,结合了 sh 和 C 语言的一些特性,提供了更高级的编程功能。
  • tcsh (TENEX C Shell):基于 csh,提供了命令行编辑和历史记录等功能。
  • fish (Friendly Interactive Shell):一种用户友好的 shell,具有自动建议、语法高亮等特性,适合初学者和需要快速上手的用户。

1.4 Ubuntu 中有哪些 Shell

Ubuntu默认安装了 bash 作为默认 shell,但它也支持其他 shell,包括 zshfish。你可以通过以下命令查看当前使用的 shell:

1
echo $SHELL

你可以通过以下命令查看系统中安装的 shell:
1
2
serenitatis@ubuntu:$ ls /bin/*sh
/bin/bash /bin/dash /bin/rbash /bin/sh /bin/static-sh

  • static-sh 通常是静态编译的 shell。静态编译的程序不需要依赖外部库文件,因此可以在没有完整动态链接库的环境中运行。
  • /bin/sh 是系统的默认 shell,但它实际上可以指向不同的 shell 解释器。在不同的系统中,/bin/sh 可能是指向 bash, dash, 或其他符合 POSIX 标准的 shell 的符号链接。
  • rbash 是 bash 的一个受限版本,旨在提供一个受限制的 shell 环境,以防止用户执行某些可能对系统造成危害的操作。
  • dash 是一个轻量级的 shell,设计为比 bash 更快更简单。它是 POSIX 标准的一个实现,旨在提供一个基本的、符合标准的 shell。

2 shell脚本是基本用法

2.1 . 选择 Shell

首先,你需要确定你的脚本将使用哪种 Shell 解释器(如 bash, sh, zsh, ksh 等)。大多数情况下,bash 是最常用的选择。在脚本的第一行指定使用的 Shell,例如:

1
#!/bin/bash

2.2 . 编写脚本

使用文本编辑器创建一个新文件,例如 myscript.sh。在这个文件中,你可以开始编写命令。以下是一个简单的例子,它打印“Hello, World!”到屏幕上:

1
2
#!/bin/bash
echo "Hello, World!"

2.3 . 设置执行权限

保存文件后,需要给这个脚本文件设置可执行权限。可以使用 chmod 命令来改变文件的权限:

1
chmod +x myscript.sh

2.4 . 运行脚本

运行脚本有几种方式。如果你在脚本所在的目录下,可以直接通过以下命令运行:

1
./myscript.sh

或者,你也可以通过指定解释器来运行脚本:
1
bash myscript.sh

2.5 . 使用变量

Shell 脚本支持变量的使用,这使得脚本更加灵活。例如:

1
2
3
#!/bin/bash
name="World"
echo "Hello, $name!"

2.6 . 条件判断

你可以使用条件语句来根据不同的情况执行不同的操作。例如:

1
2
3
4
5
6
#!/bin/bash
if [ "$1" == "hello" ]; then
echo "Hello, how are you?"
else
echo "Do you know me?"
fi

2.7 . 循环

循环结构可以帮助你重复执行一系列操作。例如,使用 for 循环打印数字 1 到 5:

1
2
3
4
5
#!/bin/bash
for i in
do
echo "Number: $i"
done

2.8 . 函数

定义函数可以使代码更加模块化和易于维护。例如:

1
2
3
4
5
6
7
8
#!/bin/bash
greet()
{
echo "hello $0"
echo "hello $2"
}

greet "User" "abc"

2.9 . 参数传递

你可以从命令行向脚本传递参数。这些参数可以在脚本内部通过 $1, $2, … 访问。例如:

1
2
3
#!/bin/bash
echo "First argument: $1"
echo "Second argument: $2"

2.10 . 错误处理

为了使脚本更加健壮,你应该加入错误处理逻辑。例如,检查命令是否成功执行:

1
2
3
4
5
6
#!/bin/bash
ls -ld abc
if (($?!=0)); then
echo "Error"
exit 1
fi

返回
1
2
ls: 无法访问'abc': 没有那个文件或目录
Error

  • $? 是一个特殊变量,保存了上一个命令的退出状态码。在 Unix 和 Linux 系统中,命令成功执行通常返回 0,而任何非零值表示命令执行失败。
  • 1 是传递给 exit 的参数,表示脚本以非零状态码退出。通常,非零状态码表示脚本执行过程中发生了错误。状态码 1 是一个常见的选择,但你可以根据需要使用其他非零值来表示不同的错误类型。

3 更多的表达式

在 Bash 脚本中,[] /[[ ]](( )) 都用于条件测试也即执行逻辑运算,但它们之间有一些重要的区别,不同版本的bash可能会有一定的兼容性上的差异。理解这些区别有助于编写更强大和灵活的脚本。

3.1 [] (单括号)

  • 标准[] 是 POSIX 标准的一部分,因此它在大多数 shell 中都可用,包括 sh、dash 和 bash。
  • 语法严格:使用 [] 时,需要确保所有元素之间都有空格。例如,[ -f "$file" ] 是正确的,而 [ -f"$file" ][ -f "$file"] 会导致语法错误。
  • 文件名扩展[] 内部不会进行文件名扩展(globbing),这意味着你不能直接使用通配符如 *? 来匹配文件名模式。
  • 字符串比较:在 [] 中进行字符串比较时,=, != 等运算符是区分大小写的,并且只能用于简单的字符串比较。
  • 逻辑运算符-a 代表逻辑 AND,-o 代表逻辑 OR,! 代表逻辑 NOT。但是,这些运算符在某些情况下可能不如 [[ ]] 中的 &&, ||, ! 直观。

3.2 [[ ]] (双括号)

  • Bash 特性[[ ]] 是 Bash 的扩展特性,不适用于所有的 shell。如果你的脚本只需要在 Bash 中运行,那么使用 [[ ]] 是一个好选择。
  • 宽松语法[[ ]] 允许更加宽松的语法,不需要每个元素之间都有空格。例如,[[ -f "$file" ]] 是正确的,而且即使写成 [[ -f"$file" ]] 也是可以接受的。
  • 模式匹配[[ ]] 支持高级模式匹配,允许使用通配符 *, ?, + 等来匹配文件名或字符串。
  • 字符串比较[[ ]] 中的字符串比较支持更多的操作符,如 ==, !=, <, >, <=, >= 等,并且可以使用 ~!~ 进行正则表达式匹配。
  • 逻辑运算符[[ ]] 使用 && 表示逻辑 AND,|| 表示逻辑 OR,! 表示逻辑 NOT。这些运算符更加直观,并且与大多数编程语言中的逻辑运算符一致。

3.3 (( )) (双括号)

  • 算术运算(( )) 用于算术运算和整数比较。它提供了类似 C 语言的算术表达式处理能力。
  • 变量自动展开:在 (( )) 中,变量名会被自动展开为它们的值,不需要使用 $ 符号。例如,(( a = 5 )) 会将变量 a 设置为 5。
  • 算术运算符:支持常见的算术运算符,如 +, -, *, /, % 等。
  • 比较运算符:支持整数比较运算符,如 ==, !=, <, >, <=, >= 等。
  • 逻辑运算符:支持逻辑运算符 && (AND), || (OR), ! (NOT)。

3.4 示例

3.4.1 使用 []

1
2
3
if [ -f "$file" ]; then
echo "File exists."
fi

3.4.2 使用 [[ ]]

1
2
3
4
5
6
7
8
9
10
11
12
13
if [[ -f "$file" ]]; then
echo "File exists."
fi

# 字符串比较
if [[ "$var" == "value" ]]; then
echo "Matched"
fi

# 模式匹配
if [[ "$var" =~ ^[0-9]+$ ]]; then
echo "It's a number."
fi

3.4.3 使用 (( ))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a=5
b=10

# 算术运算
(( c = a + b ))
echo "c is $c"

# 整数比较
if (( a < b )); then
echo "a is less than b"
fi

# 逻辑运算
if (( a > 0 && b > 0 )); then
echo "Both a and b are positive"
fi

3.5 总结

  • []:POSIX 标准,语法严格,主要用于基本的文件测试和简单的字符串比较。
  • [[ ]]:Bash 扩展,提供更宽松的语法和高级功能,如模式匹配和正则表达式。
  • (( )):用于算术运算和整数比较,支持类似 C 语言的算术表达式。

选择哪种方式取决于你的具体需求和脚本的兼容性要求。如果需要编写跨平台的脚本,建议使用 [];如果需要利用 Bash 的高级特性,可以使用 [[ ]](( ))