%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# "shell 变量" 与 "环境变量"
在 Linux 中有两种类型的变量:
- **shell 变量**(也称 "**局部变量**"):
- 定义:在**当前 shell 会话**中定义的变量
- 特点:**==仅在定义这一变量的 shell 示例中可见==**,**不会传递给子进程**。
- **环境变量**:
- 定义:**通过 `export` 命令导出或定义的 shell 变量
- 特点:
- 仅对 ==**定义该"环境变量的"shell 会话== 以及 =="定义后才启动的所有子进程== 可见;
- 对 **==定义该"环境变量的"shell 会话的父进程== 以及 ==定义该环境变量之前已运行的子进程== 都不可见;
- 在**子进程中"修改" 环境变量的值后,只对该子进程有效**,不影响其父进程中的环境变量值
> [!note] 子进程会 "继承/复制" 父进程的环境
>
> - 在子进程创建时,**系统为子进程 "==复制==" 了父进程的环境**,<br>因此父进程的全部 「**环境变量**」,其子进程也可以访问。
> >
> - 子进程中对 "**环境变量**" 的修改不会影响到父进程,而**只对==该子进程自身==有效**。
> ^ebucbz
shell 本身提供了一些内置的系统环境变量,以及一些特殊变量。
- **系统环境变量**:例如 `PATH`、`USER`、`HOME`、`IFS`、`SHELL` 等。
- **特殊变量**:例如 `$?`,`$`, `$#`,`$@`,`$*` ,`$!` 等
> **系统环境变量**基本上会使用全大写字母,例如 `HOME`,以区别于用户自定义的环境变量。因此**用户自定义变量应当以小写命名**。
<br><br><br>
# 定义变量
### (1)定义 shell 变量
- 用户自定义变量的名称可以是任何由**字母、数字或下划线**组成的字符串,长度不超过 20 个字符
- 使用等号 `=` 为变量赋值,**变量、等号、值之间不能有空格**。
- shell 脚本**以字符串形式**存储所有的变量值
- shell 脚本中定义的变量的生命周期为**整个脚本执行期间**,**脚本结束时被删除**。
```shell
var1=10
var2=-57
var3=testing
var4="still more testing"
```
#### 空变量
shell 中定义一个**空变量**通过**直接赋值一个空字符串**来实现,可以使用**空的双引号 `""` 或单引号 `''`**。
> [!NOTE] 检查变量是否为空
>
> - `-n` 验证变量值**不为空**,不为空时返回 true
> - `-z` 验证变量值**为空**,为空时返回 true
示例:
```shell
str1=Soccer
str2=''
if [ -n "$str1" ]; then
echo "The string $str1 is NOT empty"
else
echo "The string $str1 IS empty"
fi
if [-z "$str2"]; then
echo "The string $str2 IS empty"
else
echo "The string $str2 is NOT empty"
fi
```
#### 数组变量
shell 变量可以作为「**数组**」使用,即 "**存储了多个值的变量**",这些值即可以单独引用,也可以作为整体引用。
- **定义数组变量**:值放在圆括号中,值与值之间以空格分隔
- **引用单个数组元素值**:必须使用 `${var[idx]}` 形式。
```shell
my_var=(zero one two three four)
echo $mytest # 引用整体
echo ${mytest[0]} # 引用单个数组元素值
echo ${mytest[2]}
```
#### 只读变量
通过 `readonly` 或 `declare -r` 命令**在当前 shell 中**创建只读变量,**其值在创建之后就不能再更改**。
一旦变量被标记为只读,**任何试图修改它的值的操作都会导致错误**。
```sh
readonly MYVAR="value"
# 或者
declare -r MYVAR="value"
```
在 Bash 中,有些变量(如 `$BASH_VERSION`、`$SECONDS`)和特殊的 Shell 参数(如 `$RANDOM`、`$LINENO`)天然就是只读的,不能被用户修改。
### (2)定义环境变量
创建环境变量:通过 `export` 命令将一个 `shell` 变量**导出/提升为环境变量**
```shell
# 方式一: 先定义变量, 再导出
MYVAR="Hello World"
export MYVAR
# 方式二: 定义变量的同时将其导出
export MYVAR="Hello World"
```
如果希望环境变量**在系统重启后依然可用**,作为 **==用户级的或系统级别的全局环境变量==**,则需要在相应的配置文件中去定义该环境变量:
- **==用户级==环境变量**:需要将其添加到**用户的"登录配置文件"** 或 "**用户级的 shell 配置文件**" **中,`~/.bash_profile` 、 `~/.profile`、`~/.bash_login`(取决于所用 Shell 和操作系统)或 `~/.bashrc` 中。
此后每次用户登录时,变量就会自动设置。
- **==系统级==环境变量**:需要将其添加到"**系统级的登录配置文件**" 或 "**系统级的 shell 配置文件**"中,如 `/etc/profile`、`/etc/bash.bashrc` 或 `/etc/bashrc` ,或者作为脚本放到 `/etc/profile.d/` 目录中。
<br><br><br>
# 变量操作
### 查看环境变量
shell 中查看环境变量的几种方法:
- **`env` 或 `printenv` 命令**:二者功能几乎相同,都用于**显示当前用户的==环境变量**==,会**列出所有环境变量及其值**
- 可查看**特定的环境变量**,如 `printenv HOME` 或 `env | grep HOME` 或 `echo $HOME`
- **`set` 命令**:显示**当前 shell 进程**中的**所有 shell 变量**和**函数**,包括环境变量、用户自定义的局部变量等。
- **`echo` 命令**:可用于**查看指定环境变量的值**,例如 `echo $PATH` 会显示 `PATH` 环境变量的内容。
- 使用 **bash 内置命令** `declare -p` 或 `typeset- p` :
> bash 内置命令 `declare -p <VAR>` 或 `typeset -p <VAR>` 将以 "**声明**" 的形式来打印指定环境变量的值,
> 输出格式为 `declare -x VAR="value"`。
> ![[_attachment/02-开发笔记/11-Linux/shell 相关/shell 变量.assets/IMG-shell 变量-67D00949E167616F15D07B97F973E0BA.png]]
### 使用变量
shell 脚本中**引用变量值**的两种方式:
- `$variable`
- `${variable}` 带花括号,显式界定 `
后的变量名。
**引用变量值**时需要使用 `
,而**对变量赋值**时不需要使用 `
。
在一些特殊语法,例如 **`$(())` 算术表达式**或 **C 风格的 `for` 循环**中使用变量值时无需带 `
或 `${}`。
### 删除变量
使用 `unset` 命令将从 "当前 shell 会话" 中完全移除变量。
> [!NOTE] 如果是在子进程中删除一个环境变量,则该操作仅对子进程有效。该环境变量在父进程中依然存在且有效。
```sh
unset variable
```
### 变量扩展
在 Bash 和其他类 Unix shell 中,提供了一系列"**高级变量扩展**"的机制,**支持使用特定语法来操作或提取变量的内容**,包括字符串操作、条件检查等。
最基本的变量扩展即是通过 `
或 `${}` 来引用变量值。
**高级变量扩展**提供了更多功能,包括:
- **获取"变量内容"的长度**
- `${#var}`
- **==间接引用==** / **==动态变量名==**:使用动态构造的变量名
- `${!var}` ,首先获取"变量 var 的值",将这一值 (`$var`) 作为"**变量名**"再**引用该变量的值**。
- **指定默认值**:
- 如果变量**未定义或为空**,使用默认值。
- `${var:-default_value}`
- 如果变量**未定义**,使用默认值(变量为空字符串时,不会使用默认值)
- `${var-default_value}`
- **子字符串提取**:从变量中提取子字符串
- `${variable:position:length}`
- **替换**:在变量内容中替换字符串
- `${var/search/replace}`
- **条件检查**:基于变量的状态执行不同操作
- 如果变量**未定义或为空**,则**使用指定的默认值**: `:-`
- `${var:-"default_value"}`
- 如果变量**未定义或为空**,则 **将变量==赋值==为指定的默认值**: `:=`
- `${var:="default_value"}`
- 如果变量==**已设置且非空**==,则使用指定的值:`:+`
- `${var:+"assigned_value"}`
- 如果变量**未定义或为空**,则**打印错误消息并退出当前脚本**
- `${var:?"error_message"}`
- **大小写转换**(Bash 4 及以上版本):变量内容的大小写转换。
```shell
${var^} # 首字母大写
${var^^} # 全字母大写
${var,} # 首字母小写
${var,,} # 全字母小写
```
<br><br>
# bash shell 特殊变量
Bash Shell 提供了许多特殊变量,这些变量用于特定的用途,如存储命令的结果、表示当前 Shell 环境的状态等。下面是一些在 Bash 中最常用的特殊变量及其说明:
```shell
$0 # 当前脚本的名称
$1~$9, ${10}... # 命令行参数
$# # 传递给脚本的参数个数
$@ # 所有传递给脚本的参数的列表。当被双引号包围时,$@会保持参数之间的分隔,即使参数中包含空格。
$* # 所有传递给脚本的参数的列表,作为"单个字符串"。当被双引号包围时,所有参数被视为一个整体。
$? # 上一个命令的退出状态码。成功执行返回0, 失败返回非0值
$_ # 上一个命令的最后一项参数
$ # 当前shell进程的PID
$! # 最后一个在后台运行的命令的PID
$- # 当前shell的选项, 由启动时或使用`set`命令设置的标志组成
```
- `$*` 用在**双引号内**时,会被扩展成**由多个命令行参数组成的单个单词**,每个单词之间以 `IFS` 变量值的首个字符 `c` 分隔,即 `"$*"` 会被扩展成 `"$1c$2c$3c..."`
- `$@` 用在**双引号内**时,其所包含的各个命令行参数会被**扩展成独立的单词**,`"$@"` 会被扩展成 `"$1""$2""$3"...`。
```shell
#!/bin/bash
echo "Starting program at $(date)" # Date will be substituted
echo "Running program $0 with $# arguments with pid $"
for file in "$@"; do
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
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
```
<br><br><br>
# 通用环境变量
### 通用环境变量
```shell
$IFS # 内部字段分隔符. 默认包括空格、制表符和换行符
$PATH # shell 查找命令时使用的目录列表, 以冒号分隔
$PWD # 当前工作目录
$HOME # 当前用户的家目录
$USER # 当前用户的用户名
$HOSTNAME # 当前机器的主机名
$RANDOM # 返回一个随机整数
$SECONDS # 自脚本开始运行以来的秒数
$LINENO # 当前脚本中的行号
$SHELL # 当前用户的默认 shell
$TERM # 当前终端的类型
$EDITOR # 用户默认的文本编辑器
$VISUAL # 用户默认的文本编辑器
```
### shell 特有环境变量
bash shell 中自带一些默认的特定的环境变量来定义系统环境,包括:
- **Unix Bourne shell 中定义的环境变量**(由于 bash shell 源自最初的 Unix Bourne shell,因此进了保留)
> 
- **Bash shell 特有的自带环境变量**
>  
> 
> 
# 参考资料
# Footnotes