Shell:变量

介绍

在Shell脚本编程中,变量是用来存储数据的一种机制。Shell变量可以被看作是一个可以保存数据值的容器,这个值可以是字符串、数字或其他形式的数据。Shell是一种解释型的脚本语言,它不像C、Java等编译型语言那样要求变量有明确的数据类型。在Shell中,变量通常是无类型的,这意味着它们默认处理的是字符串数据。

Shell是大小写敏感的。这意味着在Shell脚本和命令行中,变量名、函数名、文件名、目录名以及命令都是区分大小写的。一个变量、命令或文件名的大小写不同,会被视为完全不同的实体。

以下是关于Shell变量的一些关键点:

  1. 定义变量

    • 变量定义不需要指定类型,直接赋值即可。
    • 格式通常为:name=value 或者 name="value"(如果value本身包含空格或特殊字符)。
    • 例如:myVar="Hello, world!"
  2. 引用变量

    • 使用变量时,需要在变量名前加上美元符号 $
    • 例如:echo $myVar 将会输出 “Hello, world!"。
  3. 变量作用域

    • 局部变量:只在当前Shell会话或脚本中有效。
    • 环境变量:可以在子Shell或子进程中继承,对整个系统或用户环境有影响。
    • 要将一个变量声明为环境变量,可以使用 export 命令。
  4. 变量类型

    • 字符串变量是最常用的类型。
    • 数值变量:虽然默认按字符串处理,但可以通过特定命令如 let 或者 (( )) 进行数学运算。
    • 数组变量:在较新的Bash版本中可用,用于存储多个值。
  5. 变量命名规则

    • 变量名可以包含字母、数字和下划线。
    • 必须以字母或下划线开头。
    • 建议使用大写或下划线分隔单词,如 MY_VARIABLE

系统定义的变量

在Shell环境中,尤其是Bash,有很多系统预定义或内置的变量,这些变量用于存储与系统和当前shell会话相关的信息。这些变量对于编写脚本和进行shell编程非常有用。以下是一些常见的Bash系统定义变量(也称为环境变量或内置变量)的概述:

  1. PATH: 定义了Shell搜索用户命令的目录列表,由冒号分隔。当用户在命令行输入一个命令时,Shell会按照PATH变量中定义的目录顺序来查找这个命令的可执行文件。
  2. HOME: 指向当前用户主目录的完整路径。它经常用于各种程序和脚本中,以访问用户的个人文件。
  3. SHELL: 当前用户登录会话的Shell路径。这通常是用户登录时启动的Shell程序,如/bin/bash
  4. USERLOGNAME: 当前用户的登录名。
  5. UID: 当前用户的用户ID,一个数字标识。
  6. PWD: 当前工作目录的完整路径。
  7. OLDPWD: 在使用cd命令更改目录之前的工作目录的路径。
  8. RANDOM: 生成一个介于0到32767之间的随机整数。每次引用时,它都会生成一个新的随机数。
  9. IFS (Internal Field Separator): 定义了在读取输入时用于字段分割的字符集合,默认是空格、制表符和换行符。
  10. PS1: 定义主提示符的外观。在默认情况下,它通常是类似user@host:dir$ 的东西。
  11. PS2: 定义第二提示符的外观,当命令需要更多输入时显示(如使用反斜杠\继续行时)。
  12. LANGLC_ALL: 用于设置程序运行时的语言环境,包括字符编码和地区设置。
  13. TERM: 定义终端的类型,用于确定终端的功能和特性。
  14. OSTYPE, MACHTYPE, HOSTNAME: 分别提供操作系统类型、机器硬件类型和主机名的信息。
  15. TMPDIRTEMPTMP: 指定用于存储临时文件的目录。
  16. EDITOR: 定义默认的文本编辑器,某些命令(如crontab -e)会使用这个变量来确定使用哪个编辑器。
  17. HISTSIZEHISTFILESIZE: 分别控制shell会话期间保存的命令历史数量和历史文件中保存的命令数量。
  18. SHELLOPTS: 列出当前设置的shell选项,如braceexpandemacs等。

请注意,不同的Unix和Linux发行版可能会定义额外的环境变量,或者在不同的上下文中使用这些变量的不同默认值。此外,用户可以在其shell配置文件中(如.bashrc.bash_profile.profile等)定义自己的变量,以自定义其shell环境。

1
2
[root@lavm-qeu42ld2gu ~]# echo $SHELL
/bin/bash

查看所有系统定义的变量

在Shell(特别是Bash)中,要查看所有系统定义(环境)的变量,你可以使用envprintenv命令。这些命令会列出当前Shell会话中的所有环境变量及其值。需要注意的是,这些命令列出的是环境变量,而不仅仅是系统预定义或“系统级”的变量;用户也可以在他们的shell配置文件中定义环境变量。然而,在大多数情况下,这些环境变量包含了系统级配置和用户级配置。

使用env命令

打开你的终端或命令行界面,并输入:

1
env

这将列出所有环境变量的名称和值。输出可能会很长,因为它包含了所有继承自操作系统和用户配置的变量。

使用printenv命令

另一个查看环境变量的命令是printenv。你可以简单地运行它而不带任何参数来列出所有环境变量:

1
printenv

env命令类似,printenv也会列出当前Shell会话中的所有环境变量。

过滤特定的环境变量

如果你只对特定的环境变量感兴趣,可以使用grep命令来过滤输出。例如,要查看与PATH相关的所有环境变量(虽然通常只有一个名为PATH的变量),你可以这样做:

1
env | grep PATH

或者

1
printenv | grep PATH

但请注意,由于PATH是一个常见的环境变量名,并且通常只定义一次,所以上面的命令实际上只会返回一行输出(如果PATH变量被定义了的话)。

注意

  • 记住,envprintenv命令列出的是环境变量,而不是Shell的所有变量。Shell还可能有其他内部变量,这些变量不会通过环境传递给子进程,但你可以使用set命令来查看这些变量。
  • 系统级变量和用户级变量之间的区别主要是从配置和来源的角度来看的。系统级变量通常由操作系统或系统管理员设置,而用户级变量则可以在用户的shell配置文件中设置。然而,在shell会话中,它们都被视为环境变量(如果它们被导出为环境变量的话)。

用户自定义变量

在Shell(特别是Bash)中,用户自定义变量的语法相对简单。你可以直接通过赋值操作来创建和设置变量。下面是一些基本的语法规则和示例。

定义变量

要定义一个变量,你只需给它指定一个名称并赋予一个值。变量名和等号之间不能有空格。值可以紧跟在等号后面,或者等号后面可以有空格(如果值是用引号括起来的话)。

1
2
# 基本语法  
variable_name=value  
1
2
# 示例  
myVar="Hello, World!"

撤销变量

1
unset 变量名

变量定义规则

  1. 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
  2. 等号两侧不能有空格。
  3. 在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算。
  4. 变量的值如果有空格,需要使用双引号或单引号括起来。

访问变量

要访问(即使用)变量的值,你需要在变量名前加上$符号。如果变量名由多个部分组成(比如包含空格或特殊字符),或者你想要在字符串内部清晰地分隔变量名和其他文本,你可以使用大括号{}将变量名括起来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 访问变量  
echo $myVar  
  
# 使用大括号(可选,但推荐用于复杂情况)  
echo ${myVar}  
  
# 在字符串中使用变量  
greeting="Hello, "  
name="Alice"  
echo $greeting$name  # 通常这样也可以,但不够清晰  
echo "${greeting}${name}"  # 更清晰的方式

只读变量

使用readonly命令可以将变量设置为只读,这意味着一旦变量被赋值,它的值就不能被改变。

1
2
3
4
5
# 定义只读变量  
readonly MY_READONLY_VAR="This variable cannot be changed"  
  
# 尝试修改只读变量(会失败)  
# MY_READONLY_VAR="Attempt to change value"  # 这会导致错误

【注意】只读变量不能unset

导出变量

如果你想让在当前Shell会话中定义的变量在子Shell或子进程中也可用,你需要使用export命令来导出这个变量。

1
2
3
4
# 定义并导出变量  
export MY_EXPORTED_VAR="This variable is exported"  
  
# 现在,MY_EXPORTED_VAR在子Shell或子进程中也是可见的

变量赋值的其他方式

  • 使用命令的结果赋值:你可以将命令的输出赋值给变量。

    1
    2
    3
    4
    
    # 使用反引号或$(...)  
    currentDate=`date`  # 旧式语法  
    currentDate=$(date)  # 推荐语法  
    echo $currentDate
  • 算术运算:对于整数,你可以使用$((expression))来进行算术运算,并将结果赋值给变量。

    1
    2
    3
    4
    5
    
    # 算术运算  
    a=5  
    b=10  
    sum=$((a + b))  
    echo $sum

请注意,Shell变量的类型是弱类型的,即变量不需要显式地声明为某种类型(如整数、浮点数或字符串)。Shell会根据上下文自动解释变量的值。

单引号和双引号的区别

在Shell脚本或命令行中,双引号(")和单引号(')都用于定义字符串,但它们之间有几个关键的区别,主要体现在Shell对它们内部内容的处理方式上。

单引号('

  1. 不转义:在单引号内的所有字符都被视为普通字符,即使它们是Shell的特殊字符(如$, ```, \, *等)。这意味着Shell不会尝试解释或展开这些字符。
  2. 不扩展变量:在单引号中,变量名不会被展开为其值。如果你在单引号中放入一个变量名,它会被当作普通的字符串处理。
  3. 不扩展命令替换:在单引号中,命令替换(比如使用反引号command$(command))不会被执行,而是被当作普通文本。
  4. 用途:当你需要确保字符串中的每个字符都被当作普通字符时,使用单引号。

双引号("

  1. 允许部分转义:在双引号中,大多数字符都会被当作普通字符,但某些特殊字符(如$, ```, \, ")有特殊的含义。你可以使用反斜杠(\)对这些特殊字符进行转义,使其被当作普通字符。
  2. 扩展变量:在双引号中,变量名会被展开为其值。这使得在字符串中嵌入变量的值变得简单。
  3. 扩展命令替换:在双引号中,命令替换(如$(command))会被执行,并且其输出会被嵌入到字符串中。
  4. 用途:当你需要在字符串中嵌入变量或命令的输出时,使用双引号。

示例

假设有一个变量name="World"

  • 使用单引号:echo '$name' 输出 $name,因为Shell不会展开变量。
  • 使用双引号:echo "$name" 输出 World,因为Shell会展开变量。

命令替换的示例:

  • 使用单引号:echo 'The current date is date' 会将date当作普通文本,输出 The current date is date``。
  • 使用双引号:echo "The current date is $(date)" 会执行date命令,并将其输出嵌入到字符串中,比如 The current date is Tue Sep 1 12:00:00 UTC 2020

特殊变量

在Shell脚本中,有一些预定义的特殊变量,它们主要用于获取脚本的运行信息、参数、环境状态等。了解和利用这些特殊变量可以帮助你编写更高效和更健壮的脚本。下面是一些常见的特殊变量及其用途:

  1. $0: 包含当前脚本的文件名。这在脚本中非常有用,尤其是当脚本从不同的位置调用时。

  2. $#: 表示传递给脚本或函数的参数数量。这对于验证参数数量是否符合预期很有用。

  3. $*"$@":

    • $* 将所有位置参数作为一个单词的列表返回,各参数之间由IFS(Internal Field Separator,内部字段分隔符)分割。
    • "$@" 将所有位置参数作为单独的单词返回,即使参数中包含空格或其他特殊字符。
  4. $1, $2, … , $n: 分别代表传递给脚本的第一个、第二个、…、第n个参数。这些参数是通过命令行传递给脚本的。

  5. $?: 包含上一个命令的退出状态码。通常用于检查命令是否成功执行(0通常表示成功,非0表示某种错误)。

  6. $$: 包含当前进程的PID(Process ID)。这在需要标识特定进程时非常有用。

  7. $!: 包含最近后台运行的命令的进程ID。这在需要跟踪或控制后台任务时很有用。

  8. $_: 包含上一条命令的最后一个参数。这在某些特定的脚本逻辑中可能会用到。

  9. $-: 包含Shell的当前选项标志。这可以让你检查Shell当前是否启用了某些选项,如交互模式、调试模式等。

  10. $+: 这不是一个变量,而是一个用于在变量名前使用以启用或禁用某些Shell选项的符号。

使用这些特殊变量可以使脚本更加灵活和动态,允许你根据运行时的条件和环境定制脚本的行为。例如,你可以使用$#$@来处理传入的参数,使用$?来检查命令的执行结果,或者使用$$来标识和管理脚本的进程。

0%