概述 Bash 的内部变量,以及在何处、如何和何时使用它们。

Section 36.1: Bash 内部变量概览

Variable Details
$* / $@ 函数/脚本位置参数(参数)。展开如下:
$* 和 $@ 与 $1 $2 相同......(请注意,不加引号通常毫无意义)
"$*" 与 "$1 $2 ..." 相同
"$@" 与 "$1" "$2" ... 相同
1. 参数由 $IFS 的第一个字符分隔,该字符不一定是空格。
$# 传递给脚本或函数的位置参数数
$! 最近进入后台的作业中最后一个(对于管道而言为最右)命令的进程 ID(注意,启用作业控制时,它不一定与作业的进程组 ID 相同)。
$$ 执行 bash 的进程的 ID
$? 最后一条命令的退出状态
$n 位置参数,其中 n=1, 2, 3, ..., 9
${n} 位置参数(同上),但 n 可以大于 9
$0 在脚本中,调用脚本的路径;用 bash -c 'printf “%s\n” “$0”' name args': name(内联脚本后的第一个参数),否则是 bash 收到的 argv[0] 。
$_ 最后一条命令的最后一个字段
$IFS 内部字段分离器
$PATH 用于查找可执行文件的 PATH 环境变量
$OLDPWD 上一个工作目录
$PWD 当前工作目录
$FUNCNAME 执行调用堆栈中的函数名数组
$BASH_SOURCE 包含 FUNCNAME 数组中元素源路径的数组。可用于获取脚本路径。
$BASH_ALIASES 包含当前定义的所有别名的关联数组
$BASH_REMATCH 从最后一个 regex 匹配开始的匹配数组
$BASH_VERSION Bash 版本字符串
$BASH_VERSINFO 由 6 个元素组成的数组,包含 Bash 版本信息
$BASH 当前执行的 Bash shell 本身的绝对路径(由 bash 根据 argv[0] 和 $PATH 的值启发式确定;在某些情况下可能是错误的)
$BASH_SUBSHELL Bash 子shell级别
$UID 运行 bash 的进程的真实用户 ID(如果不同则无效)。
$PS1 主要命令行提示符;请参阅使用 PS* 变量
$PS2 二级命令行提示符(用于额外输入)
$PS3 三级命令行提示(在选择循环中使用)
$PS4 四级命令行提示(用于将信息添加到冗余输出中)
$RANDOM 一个介于 0 和 32767 之间的伪随机整数
$REPLY 在未指定变量时,默认由 read 使用。SELECT 也会使用它来返回用户提供的值
$PIPESTATUS 数组变量,用于保存最近执行的前台流水线中每条命令的退出状态值。

变量赋值前后不能有空格。例如 a=123 和 a = 123 是不一样的,后者(用空格包围的等号)单独表示运行带有参数 = 和 123 的命令 a,但它也出现在字符串比较运算符中(从语法上讲,它是 [ 或 [[ 或您使用的任何测试的参数)。

Section 36.2: $@

“$@"会将所有命令行参数扩展为单独的单词。它与“$*”不同,后者将所有参数扩展为一个单词。

“$@"在循环使用参数和处理带空格的参数时特别有用。

假设我们在一个脚本中调用了两个参数,就像这样

$ ./script.sh "␣1␣2␣" "␣3␣␣4␣"

变量 $* 或 $@ 将扩展为 $1␣$2,进而扩展为 1␣2␣3␣4,因此循环如下:

for var in $*; do # same for var in $@; do
        echo \\<"$var"\\>
done

将同时打印

<1>
<2>
<3>
<4>

而“$*”将扩展为“$1␣$2”,进而扩展为“␣1␣2␣␣3␣␣4␣”,如此循环:

for var in "$*"; do
        echo \\<"$var"\\>
done

只会调用 echo 一次,并打印

<␣1␣2␣␣␣3␣␣4␣>

最后,“$@”将扩展为“$1”“$2”,再扩展为“␣1␣2␣”“␣3␣␣4␣”,如此循环下去

for var in "$@"; do
        echo \\<"$var"\\>
done

将打印

<␣1␣2␣>
<␣3␣␣4␣>

从而保留了参数的内部间距和参数分隔。需要注意的是,“for var in ”$@“; do ...”这种结构非常常见和习惯,它是 for 循环的默认结构,可以简写为 “for var; do ....”。

Section 36.3: $#

获取命令行参数或位置参数类型的数量:

#!/bin/bash
echo "$#"

当使用三个参数运行时,上面的示例将产生输出结果:

~> $ ./testscript.sh firstarg secondarg thirdarg
3

Section 36.4: $HISTSIZE

记录命令的最大数量:

~> $ echo $HISTSIZE
1000

Section 36.5: $FUNCNAME

要获取当前函数的名称,请输入

my_function()
{
echo "This function is $FUNCNAME" # This will output "This function is my_function"
}

如果在函数外输入该指令,则不会返回任何结果:

my_function
echo "This function is $FUNCNAME" # This will output "This function is"

Section 36.6: $HOME

用户的主目录

~> $ echo $HOME
/home/user

Section 36.7: $IFS

包含内部字段分隔符字符串,bash 在循环等情况下使用该字符串分割字符串。默认为空白字符: \n (换行符)、 \t(制表符)和空格。如果将其改为其他字符,则可以使用不同的字符来分割字符串:

IFS=","
INPUTSTR="a,b,c,d"
for field in ${INPUTSTR}; do
        echo $field
done

输出结果如下

a
b
c
d

注释 这就是所谓的分词现象。

Section 36.8: $OLDPWD

OLDPWD(OLDPrintWorkingDirectory)包含最后一条 cd 命令之前的目录:

~> $ cd directory
directory> $ echo $OLDPWD
/home/user

Section 36.9: $PWD

PWD(PrintWorkingDirectory) 您当前所在的工作目录:

~> $ echo $PWD
/home/user
~> $ cd directory
directory> $ echo $PWD
/home/user/directory

Section 36.10: $1 $2 $3 etc..

从命令行或函数传递给脚本的位置参数:

#!/bin/bash
# $n is the n'th positional parameter
echo "$1"
echo "$2"
echo "$3"

输出结果如下

~> $ ./testscript.sh firstarg secondarg thirdarg
firstarg
secondarg
thirdarg

如果位置参数的个数大于 9,则必须使用大括号。

# "set -- " sets positional parameters
set -- 1 2 3 4 5 6 7 8 nine ten eleven twelve
# the following line will output 10 not 1 as the value of $1 the digit 1
# will be concatenated with the following 0
echo $10 # outputs 1
echo ${10} # outputs ten
# to show this clearly:
set -- arg{1..12}
echo $10
echo ${10}

Section 36.11: $*

将以一个字符串返回所有位置参数。

testscript.sh:

#!/bin/bash
echo "$*"

使用多个参数运行脚本:

./testscript.sh firstarg secondarg thirdarg

输出:

firstarg secondarg thirdarg

Section 36.12: $!

后台运行的最后一项任务的进程 ID (pid):

~> $ ls &
testfile1 testfile2
[1]+ Done ls
~> $ echo $!
21715

Section 36.13: $?

最后执行的函数或命令的退出状态。通常 0 表示 OK,否则表示失败:

~> $ ls *.blah;echo $?
ls: cannot access *.blah: No such file or directory
2
~> $ ls;echo $?
testfile1 testfile2
0

Section 36.14: $$

当前进程的进程 ID (pid):

~> $ echo $$
13246

Section 36.15: $RANDOM

每次引用该参数时,都会生成一个介于 0 和 32767 之间的随机整数。为该变量赋值可为随机数生成器(源)提供种子。

~> $ echo $RANDOM
27119
~> $ echo $RANDOM
1349

Section 36.16: $BASHPID

当前 Bash 实例的进程 ID (pid)。这与 $$ 变量不同,但结果往往相同。这是 Bash 4 的新功能,在 Bash 3 中不起作用。

~> $ echo "\$\$ pid = $$ BASHPID = $BASHPID"
$$ pid = 9265 BASHPID = 9265

Section 36.17: $BASH_ENV

指向 Bash 启动文件的环境变量,在调用脚本时读取。

Section 36.18: $BASH_VERSINFO

一个包含完整版本信息的数组,分为多个元素,如果只查找主要版本,比 $BASH_VERSION 更方便:

~> $ for ((i=0; i<=5; i++)); do echo "BASH_VERSINFO[$i] = ${BASH_VERSINFO[$i]}"; done
BASH_VERSINFO[0] = 3
BASH_VERSINFO[1] = 2
BASH_VERSINFO[2] = 25
BASH_VERSINFO[3] = 1
BASH_VERSINFO[4] = release
BASH_VERSINFO[5] = x86_64-redhat-linux-gnu

Section 36.19: $BASH_VERSION

显示正在运行的 bash 版本,以便决定是否可以使用任何高级功能:

~> $ echo $BASH_VERSION
4.1.2(1)-release

Section 36.20: $EDITOR

脚本或程序将使用的默认编辑器,通常是 vi 或 emacs。

~> $ echo $EDITOR
vi

Section 36.21: $HOSTNAME

启动时分配给系统的主机名。

~> $ echo $HOSTNAME
mybox.mydomain.com

Section 36.22: $HOSTTYPE

该变量用于识别硬件,有助于确定执行哪些二进制文件:

~> $ echo $HOSTTYPE
x86_64

Section 36.23: $MACHTYPE

与上面的 $HOSTTYPE 类似,它也包括操作系统和硬件的信息

~> $ echo $MACHTYPE
x86_64-redhat-linux-gnu

Section 36.24: $OSTYPE

返回机器上运行的操作系统类型的信息,例如

~> $ echo $OSTYPE
linux-gnu

Section 36.25: $PATH

查找命令二进制文件的搜索路径。常见的例子包括 /usr/bin 和 /usr/local/bin。

当用户或脚本尝试运行命令时,系统会搜索 $PATH 中的路径,以找到具有执行权限的匹配文件。

$PATH 中的目录用“: ”分隔。

~> $ echo "$PATH"
/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin

例如,在上述 $PATH 的情况下,如果在提示符下输入 lss,shell 会依次查找 /usr/kerberos/bin/lss,然后是 /usr/local/bin/lss,然后是 /bin/lss,然后是 /usr/bin/lss,最后得出结论:没有这样的命令。

Section 36.26: $PPID

脚本或 shell 的父进程 ID (pid),即调用当前脚本或 shell 的进程。

~> $ echo $$
13016
~> $ echo $PPID
13015

Section 36.27: $SECONDS

脚本运行的秒数。如果在 shell 中显示,这个值可能会很大:

~> $ echo $SECONDS
98834

Section 36.28: $SHELLOPTS

bash 启动时提供的控制其行为的只读选项列表:

~> $ echo $SHELLOPTS

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

Section 36.29: $_

输出最后执行命令的最后一个字段,用于将某些信息传递给另一条命令:

~> $ ls *.sh;echo $_
testscript1.sh testscript2.sh
testscript2.sh

如果在其他命令之前使用,它会给出脚本路径:

test.sh:

#!/bin/bash
echo "$_"

Output:

~> $ ./test.sh # running test.sh
./test.sh

注意:这并不是获取脚本路径的万无一失的方法

Section 36.30: $GROUPS

一个数组,包含用户所在组的数量:

#!/usr/bin/env bash
echo You are assigned to the following groups:
for group in ${GROUPS[@]}; do
    IFS=: read -r name dummy number members < <(getent group $group )
    printf "name: %-10s number: %-15s members: %s\n" "$name" "$number" "$members"
done

Section 36.31: $LINENO

输出当前脚本的行号。主要用于调试脚本。

#!/bin/bash
# this is line 2
echo something # this is line 3
echo $LINENO # Will output 4

Section 36.32: $SHLVL

执行 bash 命令时,会打开一个新的 shell。环境变量 $SHLVL 保存了当前 shell 在其上运行的 shell 层数。

在新的终端窗口中,执行以下命令会根据所使用的 Linux 发行版产生不同的结果。

echo $SHLVL

使用 Fedora 25,输出结果为 “3”。这表明在打开一个新 shell 时,会执行一个初始 bash 命令并执行一项任务。初始 bash 命令会执行一个子进程(另一个 bash 命令),该子进程又会执行一个最终 bash 命令来打开新 shell。当新 shell 打开时,它是作为另外 2 个 shell 进程的子进程运行的,因此输出为 “3”。

在下面的示例中(鉴于用户运行的是 Fedora 25),新 shell 中 $SHLVL 的输出将设置为 “3”。

每执行一条 bash 命令,$SHLVL 就会递增一个。

~> $ echo $SHLVL
3
~> $ bash
~> $ echo $SHLVL
4
~> $ bash
~> $ echo $SHLVL
5

我们可以看到,执行 “bash ”命令(或执行 bash 脚本)会打开一个新的 shell。相比之下,源脚本运行的是当前 shell 中的代码。

test1.sh

#!/usr/bin/env bash
echo "Hello from test1.sh. My shell level is $SHLVL"
source "test2.sh"

test2.sh

#!/usr/bin/env bash
echo "Hello from test2.sh. My shell level is $SHLVL"

run.sh

#!/usr/bin/env bash
echo "Hello from run.sh. My shell level is $SHLVL"
./test1.sh

Execute:

chmod +x test1.sh && chmod +x run.sh
./run.sh

Output:

Hello from run.sh. My shell level is 4
Hello from test1.sh. My shell level is 5
Hello from test2.sh. My shell level is 5

Section 36.33: $UID

存储用户 ID 编号的只读变量:

~> $ echo $UID
12345