上述命令通过 echo 后的输出为:
# echo systemctl\ {restart,status}\ sshd\;
systemctl restart sshd; systemctl status sshd;
即使我将输出粘贴到终端,该命令仍然有效。但是当我尝试直接运行该命令时,我得到:
# systemctl\ {restart,status}\ sshd\;
bash: systemctl restart sshd;: command not found...
我有两个问题..
- 这种替换和扩展的方法到底叫什么? (这样我就可以研究它并了解更多关于它以及如何正确使用它的信息)。
- 我在这里做错了什么?为什么不起作用?
答案1
它是一种形式支撑扩张在外壳中完成。大括号扩展的想法是正确的,但这里使用它的方式是不正确的。当你打算这样做时:
systemctl\ {restart,status}\ sshd\;
shell 解释systemctl restart sshd;
为一个长命令并尝试运行它,但它无法找到二进制文件来以这种方式运行它。因为在这个阶段,shell 会在使用参数构建完整的命令之前尝试对命令行中的项目进行标记——但这还没有发生。
对于此类已知的扩展值,您可以使用eval
并且仍然是安全的,但请确保您尝试用它扩展什么。
eval systemctl\ {restart,status}\ sshd\;
但我宁愿使用循环来代替for
,而不是尝试写一行或使用eval
:
for action in restart status; do
systemctl "$action" sshd
done
答案2
这就是所谓的大括号扩展(如标签所示)。
我在这里做错了什么?为什么不起作用?
考虑在 bash 中读取和执行命令行所涉及的阶段(例如):
- 读取一行
- 将可能的复合命令解析为组件简单命令
- 对简单命令进行各种扩展(大括号扩展、分词、通配符等)
- 然后执行简单的命令(为了清楚起见,省略了其他阶段)。
你想要做的是影响(3)中的(2)。拆分基于;
在阶段(2),当它解析复合命令时。当大括号扩展发生 (3) 时,尝试创建复合命令已经为时已晚。
答案3
第一行
echo systemctl\ {restart,status}\ sshd\;
扩展为 3 个令牌
- 回声
- systemctl 重新启动 sshd;
- systemctl 状态 sshd;
然后 echo echo 最后两个标记,看起来不错。
同样第二行
systemctl\ {restart,status}\ sshd\;
扩展为 2 个代币
- systemctl 重新启动 sshd;
- systemctl 状态 sshd;
bash 尝试寻找systemctl restart sshd;
它无法找到的可执行文件。
您可能希望从黑暗面开始您的旅程,并eval systemctl\ {restart,status}\ sshd\;
提防意外情况。