我在Go语言中编写了一个bash任务运行程序,它的概念很简单:
1.它读取一个Taskfile
,这是一个bash脚本,包含任务定义(简单的bash函数声明)
1.它动态地添加额外的内容
1.根据传递的参数执行命令
下面是一个简化的示例:
package main
import (
"fmt"
"os/exec"
)
func main() {
//simplified for a dynamically built script
taskFileContent := "#!/bin/bash\n\ntask:foo (){\n echo \"test\"\n}\n"
// simplified for passed arguments
task := "\ntask:foo"
bash, _ := exec.LookPath("bash")
cmd := exec.Command(bash, "-c", "\"$(cat << EOF\n"+taskFileContent+task+"\nEOF\n)\"")
fmt.Println(cmd.String())
out, _ := cmd.CombinedOutput()
fmt.Println(string(out))
}
我现在的问题是,如果通过Go语言执行它,它就不起作用了,并且我得到了这个错误
task:foo: No such file or directory
但如果我直接在shell中执行生成的脚本,它就可以正常工作,如下所示:
$ /opt/opt/homebrew/bin/bash -c "$(cat << EOF
#!/bin/bash
task:foo (){
echo "test"
}
task:foo
EOF
)"
test <-- printed out from the `task:foo` above
我到底做错了什么?
2条答案
按热度按时间jxct1oxe1#
第一:这里的heredoc没有任何意义。
你什么也得不到,你不会从:
如果不使用它,代码会更简单。
秒:解释 * 为什么 *
在shell中运行以下命令时:
...
$()
周围的"
不是针对 * 正在启动 * 的bash副本的语法,而是针对 * 正在解析命令 * 的bash副本的语法。它们告诉bash副本,命令替换的结果将作为一个字符串传递,不受字符串拆分或globbing的影响。类似地,
$(cat <<EOF
、EOF
和最后的)"
都是交互式shell的指令,而不是它所调用的非交互式shell,它是运行cat
的交互式shell(具有包含连接到其stdin的Heredoc内容的临时文件),阅读cat
的该副本的stdout,然后将该数据替换为传递给bash -c
的单个参数。在你的Go语言程序中,你没有交互式环境,所以你应该使用Go语言语法--而不是shell语法--来完成所有这些步骤,而且这些步骤在Go语言中是没有必要做的(不需要将数据文件写入临时文件,也不需要让
/bin/cat
读取该文件内容,没有必要让一个子进程运行命令替换来生成一个字符串--由这些内容组成--放在最终shell的命令行上),因此忽略所有这些步骤要明智得多。jgwigjjp2#
我用printf测试了一下
这里解释了这种行为echo "#!" fails -- "event not found"
字符用于csh风格的历史扩展。您需要关闭此行为...
因此应该将
set +o histexpand
添加到.bash_profile
中