Bash中的特殊变量

Last Updated: 2023-06-24 13:54:11 Saturday

-- TOC --

Bash命令行有一些特殊变量,本文将它们汇总学习。看见$符号,就是Bash变量哈。

$?,exit code

表示上一个命令的exit code。一般0表示成功,其它表示各种失败或错误。

自己编写shell脚本,可通过exit命令输出这个值,比如:exit 0exit 1。Python的sys.exit也是输出这个值。C语言main函数中return的值也是的。

注意,exit code的取值范围是0-255,不管exit带的值是多少,最后通过echo $?都只能拿到低位的unsigned byte!因此,代码中出现exit一个负数,基本上就是一个错误,exit一个超过255的正数,也是错误。

windows下获取exit code的方法

start /wait something.exe
echo %errorlevel%

$0,$1,$2...arguments

表示输入脚本的参数。

$0表示脚本文件名;

$1开始,分别按顺序对应命令的一个参数。

进入脚本所在目录的小技巧:

dname=$(dirname $0)
cd $dname
# ...
cd -

$#,arguments number

表示参数的总的数量。

$@和$*,all arguments

这两个变量都表示全部参数,但在使用时有些不同之处。

$*$@ 没有被双引号包围时,它们之间没有任何区别,都是将接收到的参数看做一份数据,彼此之间以空格来分隔。

但是当它们被双引号""包含时,就会有区别了:

下面是我写过的一个bug,整理了一下,大家可以看出这两个符号之间的差异:

首先,一段python,通过命令行获取一个值(nargs=argparse.REMAINDER),然后打印出来:

$ cat arg.py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('cmd', nargs=argparse.REMAINDER, help='...')
args = parser.parse_args()
print(args.cmd)

然后,开始测试:

$ set 1 2 3 4
$ bash -c "python3 arg.py $@"
['1']
$ bash -c "python3 arg.py $*"
['1', '2', '3', '4']

看出区别来了吧......这个bug消耗了几个小时的生命。$*只传入一个表示全部参数的$1,$@的$1只有1。

下面这个测试会更明显:

$ cat arg.sh

function echo_arg() {
    echo '$1' $1
    echo '$2' $2
    echo '$3' $3
}

echo test '$@'
echo_arg "$@"
echo test '$*'
echo_arg "$*"
$ bash arg.sh 1 2 3 4
test $@
$1 1
$2 2
$3 3
test $*
$1 1 2 3 4
$2
$3

使用"$*"展开的命令字符串带单引号,一定要使用bach -c执行。

$ cat t.sh
set -x
echo "$*"
echo "$@"
$ bash t.sh a b 1 2
+ echo 'a b 1 2'
a b 1 2
+ echo a b 1 2
a b 1 2

$$

$$表示当前进程ID。

$PPID

看名字就知道,这是取当前进程的父进程。

$ echo $$
11919
$ echo $PPID
11917

$!

$!表示shell最后执行的后台进程的pid。常常与wait命令配合,等到某个后台进程执行完毕。

$-

$-表示当前shell参数。

A case

#!/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

!!

Entire last command, including arguments.

!!表示上一条命令,包括所有参数。

A common pattern is to execute a command only for it to fail due to missing permissions; you can quickly re-execute the command with sudo by doing sudo !!

$_

$_ - Last argument from the last command.

$ echo a b c d 1234
a b c d 1234
$ echo $_
1234
$ echo $_
1234

特殊数组

有两个特殊的bash数组

BASH_SOURCE

这个是特殊的数组。当使用source命令引入一个脚本文件的时候,$0不会改变,还是bash或者父脚本的名称,此时就要通过BASH_SOURCE来获取sourced script name!

先感受一下这个变量:

$ cat test.sh
echo ${BASH_SOURCE[@]}
echo $0
$ bash test.sh
test.sh
test.sh
$ . test.sh
test.sh
bash

通过source执行test.sh,查看$0和BASH_SOURCE的值。

当sourced script里面,继续source其它srcipt的时候,BASH_SOURCE就像一个函数调用栈一样的数组:

$ cat test.sh
echo ${BASH_SOURCE[@]}
echo $0
. kk.sh
$ cat kk.sh
echo ${BASH_SOURCE[@]}
echo $0
$ bash test.sh
test.sh
test.sh
kk.sh test.sh
test.sh
$ . test.sh
test.sh
bash
kk.sh test.sh
bash

FUNCNAME

跟BASH_SOURCE类似的特殊数组。

$ cat tf.sh
function f1() {
    echo ${FUNCNAME[@]}
}

function f2() {
    echo ${FUNCNAME[@]}
    f1
}

function f3() {
    echo ${FUNCNAME[@]}
    f2
}

f3
$ bash tf.sh
f3 main
f2 f3 main
f1 f2 f3 main
$ . tf.sh
f3 source
f2 f3 source
f1 f2 f3 source

本文链接:https://cs.pynote.net/sf/linux/shell/202201044/

-- EOF --

-- MORE --