bash - 如何理解这一行 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 node
,which 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]$
解决方案
命令
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
只有第一个有其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/null
,stdout
指向/dev/null
并stderr
指向之前的值stdout
不是控制台 /dev/null
(终端窗口);这也是之前的值stderr
。
换句话说,这错误地一直stderr
将其内容发送到终端,并且仅重定向stdout
。
正确的做法是先重定向stdout
,然后复制stdout
到stderr
:
which systemctl >/dev/null 2>&1
或者您可以使用&>
重定向运算符将两者重定向stdout
到stderr
同一个文件:
which systemctl &>/dev/null
推荐阅读
- python - 连接后 Dataframe.reset_index() 不起作用
- node.js - Node.js 使用实例名称、端口和域连接到 SQL Server
- c++ - 在 gitlab runner 上为 macos 更新 cmake
- python - 如何在python中将多行列表排序为单行列表
- c - 线程进入和线程启动之间的确切区别和关系是什么?
- python - AWS beanstalk + Django: 502 Bad Gateway - ModuleNotFoundError: No module named 'application'
- python - 切换 Python Turtle 模块
- odoo-13 - 根据many2one字段填充其他模块的一些字段
- mysql - 表引用一个表,该表又引用另一个表
- google-gsuite - 如何在 G-Suite 中批量暂停用户