如何在 Bash 中使用变量


深入理解 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_boostthis_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 目录下的文件。 我们如何修改它,使其可以统计任何目录下的文件呢? 答案是进行少量的更改。

如何在脚本中使用命令行参数

许多命令,例如 lswc,都支持命令行参数。 这些参数为命令提供额外的信息,使其知道您想要执行的操作。 例如,如果您希望 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_varsecond_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 输出变量值,它们是 alphabravo
  • script_two.sh 接收到变量后输出其值 (alphabravo)。
  • script_two.sh 将它们更改为 charliedelta
  • script_one.sh 输出变量的值,它们仍然是 alphabravo

在第二个脚本中所做的更改,只存在于第二个脚本中。 这就像是发送了一个变量的副本给第二个脚本,当脚本退出后,这个副本被丢弃。 第一个脚本中的原始变量不会受到第二个脚本中副本的影响。

如何引用变量

您可能已经注意到,脚本引用变量时会使用双引号 "。 这确保了正确引用变量,当执行该行代码时,会使用变量的实际值。

如果变量的值包含空格,在赋值时必须将它们放入引号中。 这是因为 Bash 默认使用空格作为分隔符。

以下是一个例子:

site_name=How-To Geek

Bash 会将 “Geek” 之前的空格视为新命令的开始。 它会报告没有这个命令,并且放弃该行代码。 echo 命令显示 site_name 变量不包含任何内容,即使是 “How-To” 文本。

让我们重试一次,将值用引号括起来:

site_name="How-To Geek"

这次,整个值被视为一个整体,并正确地赋值给了 site_name 变量。

echo 是您的好帮手

熟练掌握命令替换,引用变量,并记住何时使用美元符号可能需要一段时间。

在您按下回车键执行 Bash 命令之前,尝试在命令前加上 echo。 这样可以确保您将要执行的操作是您想要的。 还可以发现您在语法中可能犯的错误。