首页 > 解决方案 > 如何理解这一行 Bash 脚本?

问题描述

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

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

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

我也理解“which”是一个实用程序,它基本上通过引用 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]$ 

标签: bash

解决方案


命令

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

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

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

这基本上意味着命令被一一执行,直到其中一个失败。

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

您始终可以通过检查特殊 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

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

重定向是从左到右处理的。

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

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

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

正确的做法是先重定向stdout然后复制stdoutstderr

which systemctl >/dev/null 2>&1

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

which systemctl &>/dev/null

推荐阅读