从curl运行bash脚本失败,从文件运行bash脚本成功

从curl运行bash脚本失败,从文件运行bash脚本成功

我有一个脚本install.sh基本上如下所示:

#!/bin/bash

if [[ ! -f "/usr/bin/jq" ]]; then
  apt-get update && apt-get install -y jq
fi

echo $(date) >> install.log

如果我把它放在服务器上,然后像这样运行:

curl -s0 https://myserver.com/install.sh | bash

在疯狂的apt-get输出之后,它不会运行最后echo一行,而是将该行打印echo $(date) >> test.log为输出。而如果我将 保存install.sh在磁盘上并运行./install.sh,它会按预期运行。执行 echo 行并install.log附加 。

为了进一步重现这个问题,我在本地运行它,如下所示:

cat install.sh | bash

它也有同样的问题。

apt-get remove jq在每次测试之前手动运行。如果jq安装了,那么两者都会按预期工作。那么为什么从下载运行它与从本地文件运行它不同呢?

我录制了一个短视频来演示这个问题:https://www.youtube.com/watch?v=lYvGXI7AibA

答案1

通常bash不关心它是从文件(如bash foo)还是从 STDIN(如bash <foo)读取命令。但是现在您启动一个命令 ( apt-get),该命令本身想要询问用户并使用 STDIN 来执行此操作。

因此奇怪的事情发生了:bash 从 STDIN 读取命令并一一执行它们。现在,其中一个命令从 STDIN 读取,并且读取文件的下一行。之后 bash 从 STDIN 读取下一行,因此跳过另一个命令已经读取的一个命令。

这个小脚本将演示正在发生的事情:

echo "hello"
read -p "how do you do? "
echo "I understand"
echo "you are '$REPLY'."
echo "bye"

将其保存在文件中,然后首先运行为 ,bash -x foo然后运行为bash -x <foo.

在你的例子中也会发生同样的事情。

一种解决方案是使用进程替换:

bash -x <(cat foo)

或者在你的情况下简单地:

bash <(curl -s0 https://myserver.com/install.sh)

一些备注:

每当某个 shell 脚本做了奇怪的事情时,请使用bash -x来查看发生了什么。

写作cat foo| ...被称为猫的无用用途。只需使用重定向即可。

相关内容