如何理解这一行的Bash脚本?

时间:2018-12-12 12:03:25

标签: bash

我一直在尝试找出一个Bash脚本文件,该文件通过浏览大量文档并在线搜索来启动一些Docker容器(我对Bash还是很陌生)。但是,我根本无法弄清楚以下行的作用:

which systemctl 2>&1 >/dev/null && systemctl stop firewalld && systemctl restart docker

我了解2>&1> / dev / null部分。

我也了解“哪个”是一种实用工具,它基本上通过引用PATH信息将可执行文件的完整路径打印到STDOUT(如果我错了,请纠正我),我认为它不是最受推荐的实用工具,因为它不是在不同的UNIX-y系统上通用。

但是,我根本不理解该行。

我尝试通过输入which nodewhich docker之类的东西来玩游戏,其行为符合预期,但我完全不了解上一行的情况。

作为参考,整个代码及其输出如下:

PROVISION_PORT=3000
HLF_WORKSPACE=~/HLF_workspace

usage() {
    echo "Usage: $0 [-p provision_port] [-d HLF_workspace_path]"   # $0: 0th arg -> ./setupProvCont
}

if [ $# -eq 0 ]
then
   echo "No input parameter, will use default settings"
fi

while getopts "p:d:h" arg       #get options
do
    case $arg in
     h)
        usage
        exit        #exit 0 -> normal exit
        ;;
     p)
        PROVISION_PORT=$OPTARG  #optional arguments
        ;;
     d)
        HLF_WORKSPACE=$OPTARG
        ;;
     ?)
        usage
        exit 1     #exit 1 -> error exit, unknown flag used
        ;;
    esac
done

which systemctl 2>&1 >/dev/null && systemctl stop firewalld && systemctl restart docker
echo "Starting Provision..."
echo "docker run -it -u `id -u $USER` --network=host -e "PROVISION_PORT=$PROVISION_PORT" -e \"BCS_DOCKER_WORKSPACE=$HLF_WORKSPACE\" -e \"VM_HOSTNAME=`hostname`\" -v /var/run:/var/run -v $HLF_WORKSPACE:$HLF_WORKSPACE -d oracle/HLF-provision"
docker run -it -u `id -u $USER` --network=host -e "PROVISION_PORT=$PROVISION_PORT" -e "BCS_DOCKER_WORKSPACE=$HLF_WORKSPACE" -e "VM_HOSTNAME=`hostname`" -v /var/run:/var/run -v $HLF_WORKSPACE:$HLF_WORKSPACE -d oracle/HLF-provision 

输出:

[hlf@hlfdemo hlf_fabric]$ ./setupProvisionContainer.sh 
No input parameter, will use default settings
Starting Provision...
docker run -it -u 1000 --network=host -e PROVISION_PORT=3000 -e "BCS_DOCKER_WORKSPACE=/home/hlf/HLF_workspace" -e "VM_HOSTNAME=hlfdemo.internal" -v /var/run:/var/run -v /home/hlf/HLF_workspace:/home/hlf/HLF_workspace -d hlf/HLF-provision
b7f3073cc218b70525341c1770aaef17fc41c8e76f858807e7fd6e995594c60d
[hlf@hlfdemo hlf_fabric]$ 

1 个答案:

答案 0 :(得分:0)

命令

which systemctl 2>&1 >/dev/null && systemctl stop firewalld && systemctl restart docker

是由布尔运算符&&分隔的三个命令的AND列表。

它们从左到右执行,并且AND运算符(&&)检查其左命令的退出代码并仅在该退出代码为0时调用右命令(这表示“成功“)。

从根本上讲,这意味着命令将一一执行,直到其中一个失败为止。

在本示例中,通常使用

which来检查程序是否可用,以确保可以执行其后的命令。如果程序不可用,则不执行它们(which如果找不到程序,则以退出代码1结束)。
which的输出和错误被重定向到/dev/null(它们基本上被丢弃了),因为这里我们不需要知道systemctl的位置,只有在它存在的情况下(并且会在列表中的下一个命令直接调用时可以找到。

您始终可以通过检查特殊shell变量$?的内容来检查上一条命令的退出代码:

$ which ls
/bin/ls
$ echo $?
0
$ which some-program-that-does-not-exist
$ echo $?
1

更新

重定向分别应用于每个简单命令,并且不会影响其他命令。

此命令行中包含三个简单命令:

which systemctl 2>&1 >/dev/null
systemctl stop firewalld
systemctl restart docker

仅第一个重定向了stdoutstderr,其他两个不受影响,它们将其输出(和错误)发送到标准输出和错误流(默认情况下已连接)到终端)。

从左到右处理重定向。

首先,2>&1复制文件描述符1stdout),并将副本存储在文件描述符2stderr)下。用简单的英语来说,这意味着2>&1之后,stderr会将其内容发送到stdout相同的流中。

然后,>/dev/null更改stdout(仅stdout)以指向特殊文件/dev/null
/dev/null是一个不占用空间的特殊文件,写入该文件的所有内容都将被丢弃。将stdout和/或stderr重定向到它是丢弃输出和/或命令错误的标准Unix方法。

请注意,在2>&1 >/dev/null之后,stdout指向/dev/null,而stderr指向stdout的先前值,而不是 /dev/null,但控制台(终端窗口);这也是stderr的先前值。
换句话说,这会错误地使stderr将其内容发送到终端,并且仅重定向stdout

正确的方法是先将stdout重定向,然后 将重复的stdout重定向到stderr

which systemctl >/dev/null 2>&1

或者您可以使用&>重定向运算符将stdoutstderr都重定向到同一文件:

which systemctl &>/dev/null