我经常使用以下模式在 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
您一起管道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
从 的 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”部分这个答案简而言之,这正是你的问题)。