我创建了一个片段来测试此处的文档
$ cat test101.sh
ls
$ bash test101.sh
bmdt.md brmdh.md fild.md test test101.sh test2 test5 testfile
breakfast.md exec file.md test.sh test12 test3 test7
它来到这里文档
$ $(cat << EOF
→ ls
→ EOF)
bmdt.md brmdh.md fild.md test test101.sh test2 test5 testfile
breakfast.md exec file.md test.sh test12 test3 test7
它工作正常,
不幸的是,这不是结构化命令的情况
$ $(cat << EOF
→ for i in *
→ do
→ stat $i
→ done
→ EOF)
-bash: for: command not found
我尝试过
$ bash $(cat << EOF
→ for i in *
→ do
→ stat $i
→ done
→ EOF)
bash: for: No such file or directory
是什么问题不允许 for 命令工作?
答案1
此处文档是重定向的一种形式。在命令中,您重定向到cat
命令,然后尝试将输出用作命令替换中的命令。
$i
当此处文档的内容形成时,将对其进行扩展。这发生在文档中的循环实际运行之前很久。如果i
未设置该变量,它将扩展为空字符串。您可以选择引用此处文档(通过引用第一个EOF
as'EOF'
或\EOF
),以便其中不进行任何扩展,或者显式转义$
as\$
以防止其扩展。- 此处文档的内容将被解释为带有换行符分隔行的单个字符串。它不会经历通常的标记识别和解析普通命令所涉及的其他步骤,但会被分割成单独的单词,因为命令替换是不带引号的。特别是,
for
不会被识别为 shell 关键字。这就是为什么你的第一个失败的例子失败了。要重新评估该字符串,您必须eval
这样做,这将重新评估该字符串,就像在命令行上给出该字符串时 shell 所做的那样。 最后一个示例将扩展为
bash
后跟许多单词。第一个单词是for
,因此bash
期望运行for
在当前目录中调用的 shell 脚本,但这样做失败了。在所有的例子中,
bash
应该还抱怨这里的文档没有正确终止(因为最后一行带有EOF)
右括号,而不是EOF
),说类似的话bash: warning: here-document at line 1 delimited by end-of-file (wanted `EOF')
除非您使用的是旧
bash
版本,例如 macOS 上的默认版本。
相反,这将是更好的操作选择,因为它避免将代码转换为需要重新解释的字符串,而是将文档作为内联 shell 脚本直接提供给 shell 解释器来执行。
bash <<'END_SCRIPT'
for i in *; do
printf 'Filename: "%s"\n' "$i"
done
END_SCRIPT
您的第一个示例有效,因为它是一个简单的命令。