第一部分:基础

1. Bash 简介

1.1 Shell 是什么

Shell 是计算机操作系统中的一种用户界面,它为用户提供了与操作系统内核进行交互的方式。通过 Shell,用户可以执行命令、运行程序、管理文件系统等。Shell 是一个解释性的编程语言,它接受用户输入的命令,并将其转换为操作系统可以理解的指令。

1.2 为什么使用 Shell?

  • 命令行的强大性: Shell 提供了强大的命令行接口,使得用户可以通过简短的命令完成复杂的任务。
  • 脚本编程: 使用 Shell 脚本,用户可以编写一系列的命令,形成可重复执行的脚本,实现自动化和批量处理。
  • 系统管理: 系统管理员和开发人员常常使用 Shell 来管理和配置操作系统,执行系统维护任务。
  • 灵活性和定制性: Shell 具有高度的灵活性,用户可以根据需要定制环境,创建别名,以及编写自定义函数。

1.3 Bash 是什么

  • Bourne Shell: Bash(Bourne Again SHell)是 Bourne Shell 的一个扩展。Bourne Shell 是由 Stephen Bourne 开发的原始 Unix Shell。
  • GNU 项目: Bash 是 GNU 项目的一部分,在 1989 年由 Brian Fox 开发,目的是作为自由软件基金会 GNU 工具集的一部分。
  • 标准 Shell: Bash 是许多 Linux 发行版和 macOS 默认的标准 Shell(macos从13开始默认转向zsh),也是许多脚本和系统管理任务的首选工具。

2. Bash 的基本语法

2.1 命令行结构

Bash命令行的基本结构包括命令、选项、参数和控制操作符。

1
command [options] [arguments]
  • 命令(command(命令)): 要执行的实际命令或程序的名称。例如,lscpecho等都是命令。
  • 选项(options): 有时也称为标志或开关,用于修改命令的行为。选项通常以单个破折号(-)或双破折号(--)开始。例如,ls -l中的-l是一个选项。
  • 参数(arguments): 传递给命令的输入数据或操作对象。参数是命令的具体操作目标。例如,cp source destination中的sourcedestination就是参数。

2.2 变量

在Bash中,变量是用来存储数据值的符号名称。不同于许多其他编程语言,Bash 并不区分变量的类型。本质上说,Bash 变量是字符串,但在某些情况下,Bash 允许对变量进行算术运算和比较。

变量可以存储各种类型的数据,包括字符串、数字和数组等。变量的命名遵循一些规则:

  • 变量名是大小写敏感的。
  • 变量名可以包含字母、数字和下划线,但不能以数字开头。
  • 通常,使用大写字母表示环境变量,而使用小写字母表示用户定义的变量
2.2.1 变量赋值
  • 简单赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
variable_name=value  #等号两边不能有空格,如果有空格则认为空格后面是命令的参数,执行错误

➜ ~ age=37
➜ ~ email="$name@163.com"
➜ ~ echo $name $age
beizi 37
➜ ~ echo $name $age $email
beizi 37 beizi@163.com

# 双引号内的文本允许变量扩展和命令替换。单引号内的文本会被视为字面字符串,不进行变量扩展或命令替换。
➜ ~ email2='$name@163.com'
➜ ~ echo $email2
$name@163.com

  • 使用命令输出赋值 (命令替换)
1
2
3
4
5
6
7
8
9
var=$(command) #或
var=`command`

➜ ~ dir=$(pwd)
➜ ~ echo $dir
/Users/bz
➜ ~ dir=`pwd`
➜ ~ echo $dir
/Users/bz
  • 环境变量赋值

可以使用export命令将变量声明为环境变量,使其在脚本中和子进程中可用。

1
export MY_VARIABLE="some_value"
  • 直接读取用户输入赋值

使用read命令可以直接从用户输入中获取值并赋给变量。

1
2
3
4
5
6
7
8
9
10
11
12
➜  ~ echo "Enter your name:"
Enter your name:
➜ ~ read name
beizi
➜ ~ echo "Your name is $name"
Your name is beizi

#当然使用-p选项提示用户信息,而不用单独echo提示信息,-s隐藏用户输入信息,供密码等信息;-t设置超时时间
➜ ~ read -p "Enter you name:" name
Enter you name:beizi
➜ ~ echo $name
beizi

read 也可以同时给多个变量赋值,多个变量之间用空格分隔

1
2
3
4
5
6
7
8
9
10
➜  ~ read name age 
beizi 100
➜ ~ echo $name $age
beizi 100

# read的-a选项可以给数组赋值,值之间用空格分隔
➜ ~ read -p "Enter the animals: " -a animals
Enter the animals: cat dog pig cow horse
➜ ~ echo ${animals[@]}
cat dog pig cow horse
  • 数组赋值
1
2
3
4
5
my_array=("value1" "value2" "value3")

➜ ~ animas=(pig dog cat cow horse)
➜ ~ echo ${animals[@]}
cat dog pig cow horse
  • 默认值和替代值

可以使用${undefined_var:-default_value}来为变量设置默认值,如果变量未设置,则使用默认值。

1
2
3
4
5
6
7
8
9
export LOG_DIR="${LOG_DIR:-/var/log}" # 假设有一个环境变量LOG_DIR表示日志目录,可以这样设置默认值

input_file="${1:-default_input.txt}" # 这将使用脚本的第一个参数作为输入文件名,如果没有提供参数,则使用 default_input.txt 作为默认值。

message="User input: ${user_input:-No input provided}" #检查变量是否为空,为空时提供一个默认值
echo $message

# 配置文件中读取变量,如果未定义,则使用默认值
timeout="${config_timeout:-30}"
  • 一次为多个变量赋值

可采用read 和here stings方式或<方式为多个变量赋值

1
2
3
4
5
6
7
8
➜  ~ read name age <<< "zs 37"
➜ ~ echo $name $age
zs 37
# 也可以通过readarray方式赋值给数组
➜ ~ read -a file_name <<< $(ls)
➜ ~ echo ${file_name[@]}
command command.lsp test1.lsp test2.lsp test3.lsp

  • 间接赋值

使用间接引用可以通过变量的值构建变量名,这对于动态引用变量很有用。

1
2
3
4
5
6
7
8
9
10
11
12
bash-3.2$ var_name=age
bash-3.2$ age=37
bash-3.2$ echo ${!var_name}
37
# 使用eval命令可以执行字符串构成的命令,将字符串解释成命令执行
bash-3.2$ read -p "enter the name: " name
enter the name: beizi
bash-3.2$ read -p "enter the age: " age
enter the age: 37
bash-3.2$ eval "$name=$age"
bash-3.2$ echo "$name's age is set to ${!name}"
beizi's age is set to 37
2.2.2 引用变量
  • 通过使用”$”符号来实现变量引用
1
2
3
➜ ~ name="beizi"
➜ ~ echo $name
beizi
  • 使用花括号括起变量,可明确变量的边界
1
2
➜ ~ echo "${name}_age is $age"
beizi_age is 37

双引号中的变量会进行变量替换,而单引号则不会,会保留字符串的字面量。

使用${!var}间接引用变量,将使用var的值作为变量名引用。

  • 命令替换

使用$(command)或反引号``可以将命令的输出赋值给变量。

  • 位置参数

在脚本中,可以通过$1$2$3等位置参数引用脚本的参数

2.2.3 只读变量

可以使用readonly命令使变量变为只读,一旦变为只读,其值将不能被修改。

1
readonly hostname
2.2.4 删除变量

使用unset命令可以删除一个变量。

1
unset hostname
2.2.5 特殊变量

Bash Shell中有一些特殊的变量,它们具有特殊的含义。例如:

  • $0:脚本名称
  • $1, $2, …:脚本参数
  • $#:脚本参数的个数
  • $@: 传递给脚本或函数的所有参数的列表。
  • $*: 传递给脚本或函数的所有参数作为单个字符串,不保留参数之间的空格。
  • $?:上一个命令的退出状态,执行成功$?的值为0,否则为非零值。
  • $!:后台运行的最后一个作业的进程ID。
  • $IFS: 内部字段分隔符,用于指定字段之间的分隔符,默认为包含空格、制表符和换行符的字符串。
2.2.6 环境变量

环境变量是操作系统或特定程序所使用的动态值,其值可以在操作系统级别或在特定进程中设置。这些变量对于控制系统行为、提供配置选项以及与不同程序之间共享信息非常重要。以下是环境变量的一些详细介绍:

  1. 作用

    • 环境变量提供了一种在操作系统和应用程序之间共享配置信息的标准方式。
    • 它们允许您更改系统的行为,例如更改默认编辑器、设置语言首选项等。
  2. 设置环境变量

    • 在 Unix/Linux 系统中,可以通过在 shell 中使用 export 命令来设置环境变量。例如:export PATH=/usr/local/bin:$PATH
    • 在 Windows 中,可以通过控制面板或命令行使用 set 命令设置环境变量。例如:set PATH=C:\Program Files\MyApp;%PATH%
  3. 常见环境变量

    在 Linux 系统中,有许多常见的环境变量,它们用于控制系统行为、配置应用程序和提供系统信息。以下是一些常见的 Linux 环境变量:

    • PATH:用于指定系统在哪些目录中查找可执行文件。这是一个由冒号分隔的目录列表。

    • HOME:指定当前用户的主目录路径。

    • USER:指定当前登录的用户名。

    • LANG:指定系统默认的语言环境,影响字符编码和语言设置。

    • PWD:指定当前工作目录的路径。

    • SHELL:指定当前用户的默认 shell 的路径。

    • TERM:指定当前终端的类型,影响终端的显示行为。

    • PS1:指定 shell 提示符的格式。

    • LD_LIBRARY_PATH:用于指定动态链接器在哪些目录中查找共享库文件。

    • DISPLAY:指定 X 窗口系统的显示服务器。

    • EDITOR:指定系统默认的文本编辑器。

    • TMPDIR:指定临时文件目录的路径。

    • TZ:指定系统的时区。

    • **LC_***:一系列环境变量,用于指定系统的本地化设置,如 LC_COLLATELC_NUMERICLC_TIME 等。

    这些环境变量在 Linux 系统中广泛使用,对于系统配置、用户交互和程序运行都有重要作用。可以使用 env 命令来查看当前 shell 中的所有环境变量,或者使用 echo $VARIABLE_NAME 来查看特定环境变量的值。

  4. 环境变量的继承

    • 当一个进程创建子进程时,子进程会继承父进程的环境变量。
    • 当您在 shell 中设置环境变量时,只有当前 shell 及其子 shell 可以访问该变量。
  5. 用途

    • 控制系统行为:例如,LANG 环境变量用于设置系统的语言环境。
    • 配置应用程序:例如,PATH 环境变量用于指定系统中的可执行文件目录。
    • 共享信息:例如,某些应用程序可能会使用特定环境变量来确定配置文件的位置或其他重要信息。
  6. 查看环境变量

    • 在 Unix/Linux 中,可以使用 env 命令查看当前 shell 中的所有环境变量。
    • 在 Windows 中,可以使用 set 命令查看当前 shell 中的所有环境变量。

总的来说,环境变量在操作系统和应用程序中起着重要作用,它们提供了一种在系统中配置和共享信息的灵活方式。

2.2.7 变量的作用域

在bash中,变量的作用域有两种:全局作用域和局部作用域。

  • 在脚本最外层定义的变量具有全局作用域,任何地方都能访问这些全局变量,包括在函数内部。这些变量在整个脚本中都是可见的。
1
2
3
4
5
6
7
8
9
#!/bin/bash
name="beizi"

function sayhi {
# 函数内部访问全局变量
echo "Hello ${name}"
}
# 在脚本中调用函数
sayhi
  • 在函数内使用 local 定义的变量具有局部作用域,只能在函数内访问,无法在函数外部直接访问。函数执行完时,局部变量的生命周期也结束了。
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
name="beizi"
function sayhi {
echo "Hello ${name}"
# 定义局部变量
local age=37
echo "my age is ${age}"
}
sayhi
# 在脚本中调用局部变量失败
echo "your name is ${age}"

需要注意,如果在函数内部不使用 local 关键字声明变量,那么该变量将具有全局作用域,即使它是在函数内部定义的。使用 local 关键字可以确保变量在函数内部是局部的,而在函数外部是不可见的。

变量的作用域对于避免命名冲突、保持代码清晰和维护性非常重要。确保在需要的情况下使用局部变量,以避免不必要的全局变量。

2.3 数组

在Bash中,数组是一种有序集合,可以存储多个元素,并通过索引来访问这些元素。Bash支持一维数组,数组的元素可以包含字符串或数字。

2.3.1 声明数组

使用以下语法可声明数组:

1
arr=("element1" "element2" "element3")
2.3.2访问数组元素

通过索引可以访问数组元素,索引从0开始

1
2
echo ${arr[0]} # 输出第一个元素
echo ${arr[1]} # 输出第二个元素
2.3.3 数组长度:

获取数组的长度可以使用 `$