深入理解 Bash 变量:从基础到高级应用
对于想要编写脚本,并预先了解网络上复制粘贴的代码在 Linux 系统中的潜在影响的用户而言,变量至关重要。 本文将引导您入门,深入理解 Bash 中的变量。
变量基础
变量本质上是用来表示字符串或数值的有名称的符号。 当您在命令或表达式中使用变量时,系统会将其视为您输入了变量所存储的值,而非变量的名称本身。
创建变量非常简单,只需为其指定一个名称和一个值即可。 变量名应具有描述性,能提醒您它们所存储的数据。 变量名不能以数字开头,并且不能包含空格,但可以使用下划线开头。 除此之外,您可以使用任何字母数字字符组合(包括大小写字母)。
变量赋值示例
以下示例中,我们创建了五个变量。 赋值的格式是:变量名、等号“=”、以及要存储的值。 需要注意的是,等号两边不能有空格。 将值赋予变量的过程通常被称为“为变量赋值”。
我们将会创建四个字符串变量以及一个数值型变量 this_year
:
me=Dave
my_boost=Linux
him=Popeye
his_boost=Spinach
this_year=2023
要查看变量中存储的值,可以使用 echo
命令。 当您需要引用变量的值时,必须在变量名称前添加美元符号 $
,如下所示:
echo $me
echo $my_boost
echo $this_year
让我们一次性使用所有变量:
echo "$my_boost 对于 $me,如同 $his_boost 对于 $him (c) $this_year"
变量的值会替换变量名称。 您也可以随时更改变量的值。 例如,要为 my_boost
变量赋予新的值,只需重复赋值操作,如下所示:
my_boost=Tequila
如果您再次执行之前的命令,将会得到不同的结果:
echo "$my_boost 对于 $me,如同 $his_boost 对于 $him (c) $this_year"
因此,通过改变变量的值,您可以使用相同的命令,获得不同的结果。
关于变量的引用,请记住以下几点:
- 单引号
'
内的变量会被视为字面字符串,而非变量。 - 双引号
"
内的变量会被视为变量。 - 使用
$
符号来获取变量中存储的值。 - 不使用
$
符号的变量,仅表示变量的名称本身。
您还可以创建一个新变量,其值从现有变量或多个变量中获取。 以下命令定义了一个名为 drink_of_the_Year
的新变量,并将其赋值为 my_boost
和 this_year
变量组合的值:
drink_of_the_Year="$my_boost $this_year"
echo $drink_of_the_Year
如何在脚本中使用变量
在脚本中,变量是不可或缺的。 它们使得脚本可以处理不同的情况,提高代码的通用性。 为了说明这一点,让我们看一个计算 /dev
目录中文件数量的脚本。
将以下内容写入文本文件,并保存为 fcnt.sh
(意为“文件计数”):
#!/bin/bash folder_to_count=/dev file_count=$(ls $folder_to_count | wc -l) echo $folder_to_count 目录下有 $file_count 个文件
在运行脚本之前,需要使其可执行:
chmod +x fcnt.sh
输入以下命令来执行脚本:
./fcnt.sh
这个脚本会输出 /dev
目录下文件的数量。 以下是脚本的工作原理:
- 定义了一个名为
folder_to_count
的变量,并将字符串/dev
赋值给它。 - 定义了另一个名为
file_count
的变量,其值来自命令替换。 这是指$( )
括号之间的命令。 注意,第一个括号前有一个$
符号。$( )
结构会执行括号内的命令,并返回最终值。 在本例中,该值被赋值给file_count
变量。file_count
变量只负责接收一个值,并不关心这个值是如何获得的。 - 在命令替换中执行的命令是
ls
,它会列出folder_to_count
变量(值为/dev
)所指定的目录下的文件。因此,脚本执行的是命令ls /dev
。 ls
命令的输出通过管道传递给wc
命令。-l
(行数)选项使得wc
命令统计ls
命令输出中的行数。 由于每个文件都显示在单独的一行上,这等同于计算/dev
目录中文件和子目录的数量。 该值被赋值给file_count
变量。- 最后一行使用
echo
命令输出结果。
但这个脚本只能统计 /dev
目录下的文件。 我们如何修改它,使其可以统计任何目录下的文件呢? 答案是进行少量的更改。
如何在脚本中使用命令行参数
许多命令,例如 ls
和 wc
,都支持命令行参数。 这些参数为命令提供额外的信息,使其知道您想要执行的操作。 例如,如果您希望 ls
命令在您的主目录中执行,并显示隐藏文件,可以使用命令 ls ~ -a
,其中 ~
和 -a
是命令行参数。
我们的脚本也可以接受命令行参数。 第一个参数用 $1
表示,第二个参数用 $2
表示,以此类推,第九个参数用 $9
表示。 (实际上,还有一个 $0
变量,它始终存储脚本的名称)。
您可以在脚本中像引用普通变量一样引用命令行参数。 让我们修改脚本,并将其保存为 fcnt2.sh
:
#!/bin/bash folder_to_count=$1 file_count=$(ls $folder_to_count | wc -l) echo $folder_to_count 目录下有 $file_count 个文件
现在,folder_to_count
变量的值被设置为第一个命令行参数 $1
的值。
脚本的其余部分与之前完全相同。 现在的脚本不再是一个针对特定目录的解决方案,而是一个通用的解决方案。 您可以将其用于任何目录,因为它不再硬编码为只能处理 /dev
目录。
以下是使脚本可执行的方法:
chmod +x fcnt2.sh
现在,尝试用不同的目录运行脚本。 您可以先用 /dev
目录测试,以确保获得与之前相同的结果。 输入以下内容:
./fcnt2.sh /dev
./fcnt2.sh /etc
./fcnt2.sh /bin
对于 /dev
目录,您应该会得到与之前相同的结果。 您还可以获得其他命令行参数所指定的目录的相应结果。
为了缩短脚本,您可以完全省略 folder_to_count
变量,并在脚本中直接引用 $1
:
#!/bin/bash file_count=$(ls $1 | wc -l) echo $1 目录下有 $file_count 个文件
使用特殊变量
我们之前提到过 $0
,它始终存储脚本的文件名。 这允许您在脚本被重命名后,仍然正确打印其名称。 在日志记录的情况下,如果您想知道生成条目的进程的名称,这将非常有用。
以下是其他的特殊预定义变量:
$# |
传递给脚本的命令行参数的数量。 |
$@ |
传递给脚本的所有命令行参数。 |
$? |
最后执行的进程的退出状态。 |
$$ |
当前脚本的进程 ID (PID)。 |
$USER |
执行脚本的用户的用户名。 |
$HOSTNAME |
运行脚本的计算机的主机名。 |
$SECONDS |
脚本运行的秒数。 |
$RANDOM |
返回一个随机数。 |
$LINENO |
返回脚本的当前行号。 |
您想在一个脚本中看到所有这些变量吗? 当然可以! 将以下内容保存为名为 special.sh
的文本文件:
#!/bin/bash echo "共有 $# 个命令行参数" echo "它们是: $@" echo "第一个参数是: $1" echo "脚本名称是: $0" # 随便执行一个命令,以便我们报告其退出状态 pwd echo "pwd 命令返回 $?" echo "此脚本的进程 ID 是 $$" echo "脚本由用户 $USER 启动" echo "它在主机 $HOSTNAME 上运行" sleep 3 echo "已运行 $SECONDS 秒" echo "随机数是: $RANDOM" echo "这是脚本的第 $LINENO 行"
输入以下命令使其可执行:
chmod +x special.sh
现在,您可以使用不同的命令行参数来运行它,如下所示。
环境变量
Bash 使用环境变量来定义和记录它在启动时创建的环境的属性。 这些变量存储了 Bash 可以轻松访问的信息,例如您的用户名、语言环境、历史记录可以保存的命令数量、默认编辑器等等。
要查看 Bash 会话中的活动环境变量,请使用以下命令:
env | less
浏览列表,您可能会发现一些对脚本有用的变量。
如何导出变量
当脚本运行时,它在自己的进程中执行,并且它使用的变量在该进程之外是不可见的。 如果您想与您的脚本启动的另一个脚本共享变量,您必须导出该变量。 以下通过两个脚本展示如何实现。
首先,将以下内容保存到名为 script_one.sh
的文件中:
#!/bin/bash first_var=alpha second_var=bravo # 检查变量的值 echo "$0: first_var=$first_var, second_var=$second_var" export first_var export second_var ./script_two.sh # 再次检查变量的值 echo "$0: first_var=$first_var, second_var=$second_var"
该脚本会创建两个变量 first_var
和 second_var
,并为其赋值。 然后,脚本会将这些变量输出到终端,导出变量,然后调用 script_two.sh
。当 script_two.sh
脚本结束后返回 script_one.sh
脚本时,它再次将变量输出到终端。 通过比较输出可以了解变量是否被修改过。
我们将会使用的第二个脚本是 script_two.sh
。 它是 script_one.sh
调用的脚本。 输入以下内容:
#!/bin/bash # 检查变量的值 echo "$0: first_var=$first_var, second_var=$second_var" # 设置新的值 first_var=charlie second_var=delta # 再次检查变量的值 echo "$0: first_var=$first_var, second_var=$second_var"
第二个脚本会输出两个变量的值,然后为它们分配新值,最后再次输出。
要运行这些脚本,您需要输入以下命令使其可执行:
chmod +x script_one.sh chmod +x script_two.sh
现在,输入以下命令来运行 script_one.sh
:
./script_one.sh
输出结果如下所示:
script_one.sh
输出变量值,它们是alpha
和bravo
。script_two.sh
接收到变量后输出其值 (alpha
和bravo
)。script_two.sh
将它们更改为charlie
和delta
。script_one.sh
输出变量的值,它们仍然是alpha
和bravo
。
在第二个脚本中所做的更改,只存在于第二个脚本中。 这就像是发送了一个变量的副本给第二个脚本,当脚本退出后,这个副本被丢弃。 第一个脚本中的原始变量不会受到第二个脚本中副本的影响。
如何引用变量
您可能已经注意到,脚本引用变量时会使用双引号 "
。 这确保了正确引用变量,当执行该行代码时,会使用变量的实际值。
如果变量的值包含空格,在赋值时必须将它们放入引号中。 这是因为 Bash 默认使用空格作为分隔符。
以下是一个例子:
site_name=How-To Geek
Bash 会将 “Geek” 之前的空格视为新命令的开始。 它会报告没有这个命令,并且放弃该行代码。 echo
命令显示 site_name
变量不包含任何内容,即使是 “How-To” 文本。
让我们重试一次,将值用引号括起来:
site_name="How-To Geek"
这次,整个值被视为一个整体,并正确地赋值给了 site_name
变量。
echo
是您的好帮手
熟练掌握命令替换,引用变量,并记住何时使用美元符号可能需要一段时间。
在您按下回车键执行 Bash 命令之前,尝试在命令前加上 echo
。 这样可以确保您将要执行的操作是您想要的。 还可以发现您在语法中可能犯的错误。