var*_*der 2 bash source remote control-flow case
我经常使用这种模式在 GitHub 中执行原始版本的远程 Bash 脚本:
wget -O - https://raw.githubusercontent.com/<username>/<project>/<branch>/<path>/<file> | bash
Run Code Online (Sandbox Code Playgroud)
通常我可以毫无问题地做到这一点,但是由于我将以下代码添加到某个脚本中,我得到了一个无限循环echo(即使我只是将它从 GitHub 复制粘贴到终端并直接执行,它也经常发生):
wget -O - https://raw.githubusercontent.com/<username>/<project>/<branch>/<path>/<file> | bash
Run Code Online (Sandbox Code Playgroud)
我至少可以部分解决这个问题,例如:
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
Run Code Online (Sandbox Code Playgroud)
这表明| bash管道至少会使问题恶化,因为问题总是伴随着它发生。
为什么会echo "Please answer yes or no."“无休止地”发生?(我用CTRLC+停止它C)
您在单行执行命令和/或while true; do case esac done?
为什么会
echo …“无休止地”发生?
随着wget … | bash你的管道wget到bash. 的标准输入bash来自wget. read从同一个标准输入读取。
一般来说,read从脚本的来源读取会消耗脚本的一部分。在您的情况下bash需要阅读整个while … done片段(因为例如它可能是while … done <whatever)。当read工作时,没有什么可阅读的。即使第一个read失败。
有问题的脚本不检查是否read失败。
另外将read -p其提示打印到标准输入,因此您永远不会看到提示。
如果剧本有</dev/tty read …或while … done </dev/tty再read不会从标准输入读取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 - …)
Run Code Online (Sandbox Code Playgroud)
取决于您是希望脚本在单独的bash(例如在您的wget … | bash …try 中)还是在当前的 shell 中(例如在您的source FILENAME解决方案中)运行。
请注意,您现在可以独立重定向标准输入,例如:
yes | bash <(wget -O - …)
Run Code Online (Sandbox Code Playgroud)
该<(some_command …)语法称为进程替换。它适用于 Bash和少数其他 shell,但不适用于纯 shell sh(请参阅bashisms)。这里有一些有趣的观察:过程替换和管道(这个答案的“保留标准输入”部分简而言之就是你的问题)。