从代码存储库执行远程脚本会导致无限循环

从代码存储库执行远程脚本会导致无限循环

我经常使用以下模式在 GitHub 中执行远程 Bash 脚本的原始版本:

wget -O - https://raw.githubusercontent.com/<username>/<project>/<branch>/<path>/<file> | bash

一般来说,我可以毫无问题地做到这一点,但由于我将以下代码添加到某个脚本中,我得到了一个无限循环echo(即使我只是将其从 GitHub 复制粘贴到终端并直接执行,它也经常发生):

while true; do
    read -p "Example question: Do you wish to edit the PHP file now?" yn
    case $yn in
        [Yy]* ) nano PROJECT/PHP_FILE; break;;
        [Nn]* ) break;;
        * ) echo "Please answer yes or no.";;
    esac
done

我至少可以通过以下方式部分解决问题:

cd ACTION_DIRECTORY
wget https://raw.githubusercontent.com/<username>/<project>/<branch>/<path>/<file> &&
source FILENAME &&
rm FILENAME

这表明| bash管道至少使问题恶化,因为问题总是随之而来。

为什么会echo "Please answer yes or no."“无休无止”地发生? (我用CTRLC+停止它C
您在单行执行命令和/或 中发现任何问题吗while true; do case esac done

答案1

为什么会echo …“无休无止”地发生?

wget … | bash您一起管道wgetbash.的标准bash输入来自wget.read从相同的标准输入读取。

一般来说,read从脚本来源处读取可能会消耗脚本的部分内容。在您的情况下bash需要读取整个while … done片段(因为例如它可能是while … done <whatever)。工作的时候read,就没有什么可读的了。即使第一次也read失败了。

有问题的脚本不会检查是否read失败。

另外,read -p将其提示打印到标准输入,因此您永远不会看到提示。

如果脚本中存在</dev/tty read …while … done </dev/tty不会read从 的 stdin 读取bash,它将从控制台读取。这可行,但该方法需要更改脚本本身。一般来说,如果您以不同的方式运行脚本并且需要read从整个脚本的 stdin 中读取(恰好与/dev/tty.

但随后nano(如果您回答y)可能会抱怨其标准输入。如果您的修复是</dev/tty read …那么nano将会产生,Too many errors from stdin因为它的标准输入将是来自 的(已经损坏的)管道wget。如果你的修复是while … done </dev/tty那么它就会起作用。

对于更大的脚本,可能有更多的地方可以通过这种方式“修复”。一个正确的通用解决方案是根本不劫持标准输入。

通用解决方案是运行以​​下之一:

bash <(wget -O - …)
.    <(wget -O - …)

取决于您是否希望脚本在单独的 shell 中运行bash(如在您的wget … | bash …尝试中)或在当前 shell 中运行(如在您的source FILENAME解决方案中)。

请注意,您现在可以独立重定向标准输入,例如:

yes | bash <(wget -O - …)

语法<(some_command …)称为流程替代。有用在重击中以及其他一些外壳,但不是纯粹的sh(参见巴什主义)。这里有一些有趣的观察:进程替换和管道(“保留 STDIN”部分这个答案简而言之,这正是你的问题)。

相关内容