zoukankan      html  css  js  c++  java
  • 常见 Bash 内置变量介绍

    目录

    $0
    $1, $2 等等
    $#
    $* 与 "$*"
    $@ 与 "$@"
    $!
    $_
    $$
    $PPID
    $?
    $BASH
    $BASH_VERSION
    $EUID 与 $UID
    $GROUPS
    $HOME
    $HOSTNAME
    $IFS
    $PATH
    $OLDPWD
    $PWD
    $PS1
    $PS2
    $PS4

    $0

    执行 Bash 脚本时,Bash 会自动将脚本的名称保存在内置变量 $0 中。因为 $0 基于的是实际的脚本文件名称,而不是在脚本中进行硬编码,所以在重命名脚本文件的名称后,不需要修改脚本的内容。比如下面的脚本片段:

    #!/bin/bash
    
    ARGS=3 # 这个脚本需要 3 个参数.
    E_BADARGS=65 # 传递给脚本的参数个数不对.
    echo "Args number is : $#"
    echo $0
    if [ $# -ne "$ARGS" ]
    # 测试脚本的参数个数。
    then
        echo "Usage: $(basename $0) first-parameter second-parameter third-parameter"
        exit $E_BADARGS
    fi
    # 开始干正事儿

    在上面的代码中我们使用了 $(basename $0) 的写法,这是因为 $0 会包含脚本文件的路径,为了让输出看起来清爽一些,我用 $(basename $0) 去掉了脚本的路径名称,下面是运行的结果:

    $1, $2 等等

    $0, $1,$2... 被称为位置参数。所谓的位置参数(positional parameter),指的是 Shell 脚本的命令行参数(argument);同时也表示在 Shell 函数内的函数参数。它们的名称是以单个的整数来命名。出于历史的原因,当这个整数大于 9 时,就应该以大括号{} 括起来。下面是一个简单的 demo:

    #!/bin/bash
    echo $1
    echo $2
    echo $3

    $#

    位置参数的个数,具体的用法请参考 $0 中的示例。

    $* 与 "$*"

    所有的位置参数。但是 $* 与 "$*" 的表现是不一样的,我们通过下面的 demo 来介绍其异同。
    $* 提供分隔后的参数:

    for arg in $*
    do
        echo $arg
    done

    $* 和 $@ 的表现是一样的。

    "$*" 把所有参数看作一个字符串:

    for arg in "$*"
    do
        echo $arg
    done

    $@ 与 "$@"

    所有的位置参数。$@ 和 $* 的表现是一样的。
    "$@" 能够提供看上去比较合理的结果:

    for arg in "$@"
    do
        echo $arg
    done

    下面是 "$@" 的一个比较常见的用法如下:

    if[ "$1"='node' ]; then
        SCRIPT_FILE=
        for ARG in "$@"
        do
            if[ "${ARG}"='main.js' ]; then
                SCRIPT_FILE='main.js'
                break
            fi
        done
        if[ -z "$SCRIPT_FILE" ]; then
            exec "$@""main.js"
            exit 0;
        fi
    fi
    exec"$@"

    这是在常见 nodejs 的 docker 镜像时经常使用的一段代码:

    "$@" 还常常与 shift 命令一起使用来丢弃参数 $1
    #!/bin/bash
    # 使用./test.sh 1 2 3 4 5 来调用这个脚本
    echo "$@" # 1 2 3 4 5
    shift
    echo "$@" # 2 3 4 5
    shift
    echo "$@" # 3 4 5
    # 每次 "shift" 都会丢弃$1.
    # "$@" 将包含剩下的参数. 

    还可以使用 set 命令在脚本中设置位置参数:

    #!/bin/bash
    
    set -- "First one" "second" "third:one" "" "Fifth: :one"
    # 设置这个脚本的参数, $1, $2, 等等.
    index=1 # 起始计数.
    echo "Listing args with "$@":"
    for arg in "$@"
    do
        echo "Arg #$index = $arg"
        let "index+=1"
    done # $@ 把每个参数都看成是单独的单词.
    echo "Arg list seen as separate words."

    $!

    运行在后台的最后一个作业的 PID。

    $ sleep 60 &
    [1] 6238
    $ echo "$!"
    6238

    如果有多个在后台运行的任务,就需要通过 $! 来获得 PID 并进行 wait:

    $ sleep 60 &
    $ pid1=$!
    $ sleep 100 &
    $ pid2=$!
    $ wait $pid1      # 等待第一个后台进程结束
    $ wait $pid2      # 等待第二个后台进程结束

    $_

    这个变量保存之前执行的命令的最后一个参数的值。
    把下面的代码保存在 test.sh 文件中:

    #!/bin/bash
    
    echo $_ # ./test.sh
    
    du >/dev/null # 这么做命令行上将没有输出.
    echo $_ # du
    
    ls -al >/dev/null # 这么做命令行上将没有输出.
    echo $_ # -al (这是最后的参数)
    
    :
    echo $_ # :

    下面是一个比较常见的用法,可以直接进入创建的目录:

    $ mkdir hello && cd $_

    $$

    脚本自身的 PID (当前 bash 进程的 PID):

    $PPID

    进程的 $PPID 就是这个进程的父进程的 PID。

    $?

    $? 保存了最后所执行的命令的退出状态码,一般表示命令执行成功或失败。当函数返回之后,$? 保存函数中最后所执行的命令的退出状态码。这就是 bash 对函数 "返回值" 的处理方法。当一个脚本退出,$? 保存了脚本的退出状态码,这个退出状态码也就是脚本中最后一个执行命令的退出状态码。 0 表示成功,其它值表示错误。

    当脚本以不带参数的 exit 命令来结束时,脚本的退出状态码就由脚本中最后执行的命令来决定(就是exit之前的命令)。不带参数的exit命令与 exit $? 的效果是一样的,甚至脚本的结尾不写 exit,也与前两者的效果相同。
    我们还可以把 $? 保存到变量中,从而让脚本返回其中某个命令的返回值:

    #!/bin/bash
    set -x
    go get -d -v golang.org/x/net/html
    go get -u github.com/jstemmer/go-junit-report
    go test -v 2>&1 > tmp
    status=$?
    $GOPATH/bin/go-junit-report < tmp > test_output.xml
    
    exit ${status}

    上面的程序把 go test 命令的返回值保存到了变量 status 中,并通过 exit ${status} 作为脚本的返回值。

    关于退出状态
    在 Linux 系统中,程序(包括脚本)的退出状态是非常有用的,只要程序执行完成,就会向 Shell 返回一个退出状态码。这个状态码是一个数值,指明了程序是否成功结束。按照惯例,退出状态码为 0 表示程序运行成功;非 0 表示程序运行失败,不同的值对应着不同的失败原因。
    造成程序运行失败的原因可能是非法参数,也可能是出现了错误的条件。比如 cp 命令,退出状态码 1 表示文件没有找到,2 表示文件不可读,3 表示目标目录没有找到,4 表示目标目录不可写,5 表示一般性错误。

    $BASH

    Bash 的二进制程序文件的路径:

    $BASH_VERSION

    检查系统上安装的 Bash 版本号:

    检查 $BASH_VERSION 对于判断系统上到底运行的是哪个 shell 来说是一种非常好的方法。变量 $SHELL有时候不能够给出正确的答案。

    $EUID 与 $UID

    $EUID 表示 "有效" 用户 ID。

    $UID 表示 用户ID号,是当前用户的用户标识号, 记录在 /etc/passwd 文件中。这是当前用户的真实 id, 即使只是通过使用 su 命令来临时改变为另一个用户标识, 这个 id 也不会被改变。$UID 是一个只读变量,不能在命令行或者脚本中修改它。

    $GROUPS

    当前用户所属的组。
    这是一个当前用户的组 id 数组, 与记录在 /etc/passwd 文件中的内容一样:

    $HOME

    用户的 home 目录,一般是 /home/username。

    $HOSTNAME

    主机名称。

    $IFS

    内部域分隔符。这个变量用来决定 Bash 在解释字符串时如何识别域,或者单词边界。
    $IFS默认为空白(空格, 制表符,和换行符),但这是可以修改的,比如在分析逗号分隔的数据文件时,就可以设置为逗号。注意 $* 使用的是保存在 $IFS 中的第一个字符来分隔位置参数的。
    $IFS 处理其他字符与处理空白字符不同的 demo:

    #!/bin/bash
    
    output_args_one_per_line()
    {
        for arg
            do echo "[$arg]"
        done
    }
    
    echo "IFS=" ""
    echo "-------"
    
    IFS=" "
    var=" a b c "
    output_args_one_per_line $var
    echo; echo "IFS=:"
    echo "-----"
    
    IFS=:
    var=":a::b:c:::" # 与上边一样, 但是用" "替换了":".
    output_args_one_per_line $var
    # 使用 : 后,冒号前后的空字符也被解析了。
    exit 0

    执行上面的脚本,结果如下:

    $PATH

    可执行文件的搜索路径。
    当给出一个命令时,Bash 会自动生成一张哈希(hash)表,并且在这张哈希表中按照 PATH 变量中所列出的路径来搜索这个可执行命令。路径会存储在环境变量中,$PATH 变量本身就一个以冒号分隔的目录列表。通常情况下,系统都是在 /etc/profile 和 ~/.bashrc 中存储 $PATH 的定义,Ubuntu 是定义在 /etc/environment 文件中。

    PATH=${PATH}:/opt/bin

    将会把目录 /opt/bin 附加到当前目录列表中,在脚本中,这是一种把目录临时添加到 $PATH 中的权宜之计。当这个脚本退出时,$PATH 将会恢复以前的值(一个子进程,比如说一个脚本,是不能够修改父进程的环境变量的)。

    当前的"工作目录",通常是不会出现在 $PATH 中的,这样做的目的是出于安全的考虑。因为当前目录是不断变化的,很有可能会存在与系统工具同名的恶意程序(比如你在网上下载了一个叫 cat 的恶意程序)。这时执行 cat 命令,就会运行当前目录下的 cat 恶意程序(把当前目录放在 PATH 变量的靠前位置的情况)。
    还有一种情况,比如我们经常会自己用c语言或者其它的语言写一些程序,然后编译、链接为可执行文件。假如我们的可执行文件是做一些不可恢复性的操作,比如删除文件,格式化磁盘之类的。而这些文件名字又恰巧和我们系统 $PATH 下的某些常用可执行文件名字相同时,那么结果会出乎我们的意料。
    也就是说当前目录是总在变化的,一会我们 cd 到这儿了,一会又 cd 到另一个地方去了。这样的话,当前目录下有哪些可执行文件也会随着改变的。有时候我们不会太在意自己处于的目录位置,如果当前目录在 $PATH中,那么我们也就不清楚自己干了什么。

    而 $PATH 里面则放置了一些固定的目录,这些目录是不会变化的,这样的话,当我们输入命令时,永远可以保证不会随着自己的位置改变,而导致出乎意料。

    $OLDPWD

    前一个工作目录,可以通过下面的命令快速的回到前一个工作目录:

    $ cd -

    $PWD

    工作目录(你当前所在的目录),这与内置命令 pwd 的作用相同:

    下面的脚本演示了如何防止误删文件:

    #!/bin/bash
    
    E_WRONG_DIRECTORY=73
    clear # 清屏.
    TargetDirectory=/home/nick/testdir
    cd $TargetDirectory
    echo "Deleting stale files in $TargetDirectory."
    
    if [ "$PWD" != "$TargetDirectory" ]
    then # 防止偶然删错目录.
        echo "Wrong directory!"
        echo "In $PWD, rather than $TargetDirectory!"
        echo "Bailing out!"
        exit $E_WRONG_DIRECTORY
    fi
    
    rm -rf * # 删除文件
    rm .[A-Za-z0-9]* # 删除点文件
    
    echo "Done."
    echo "Old files deleted in $TargetDirectory."
    exit 0

    执行上面的脚本,显示的结果如下:

    $PS1

    这是主提示符,可以在命令行中见到它,笔者的 Ubuntu16.04 中为:

    看起来有些复杂,其实是添加了一些字体颜色的设置等内容。

    $PS2

    第二提示符,当你需要额外输入的时候,你就会看到它,默认值为 ">":

    当我们往命令行上粘贴一个多行的命令时就会看到它的身影:

    $PS4

    第四提示符,当我们使用 -x 选项来调用脚本时,这个提示符会出现在每行输出的开头,默认为 "+":

    运行下面的脚本:

    set -x
    echo "Hello nick"
    echo 'This will show $PS4'

    参考:
    Bash Internal Variables
    《高级 Bash 脚本编程指南》
    《Unix/Linux/OS X 中的 Shell 编程》

  • 相关阅读:
    实现自动进行金额汇总
    实现模糊查询
    手电筒查询
    lov的建立
    日历 的建立
    快速创建Folder
    TAB页制作
    堆叠画布
    弹性域的开发
    注销记录的实现
  • 原文地址:https://www.cnblogs.com/sparkdev/p/9934595.html
Copyright © 2011-2022 走看看