shell 使用Go语言执行动态bash脚本,在一行中包含函数声明

ogsagwnx  于 2022-12-23  发布在  Shell
关注(0)|答案(2)|浏览(188)

我在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

我到底做错了什么?

jxct1oxe

jxct1oxe1#

第一:这里的heredoc没有任何意义。

你什么也得不到,你不会从:

cmd := exec.Command(bash, "-c", taskFileContent+"\n"+task)

如果不使用它,代码会更简单。

秒:解释 * 为什么 *

在shell中运行以下命令时:

/opt/opt/homebrew/bin/bash -c "$(cat << EOF
#!/bin/bash

task:foo (){
  echo "test"
}

task:foo
EOF
)"

... $()周围的"不是针对 * 正在启动 * 的bash副本的语法,而是针对 * 正在解析命令 * 的bash副本的语法。它们告诉bash副本,命令替换的结果将作为一个字符串传递,不受字符串拆分或globbing的影响。
类似地,$(cat <<EOFEOF和最后的)"都是交互式shell的指令,而不是它所调用的非交互式shell,它是运行cat的交互式shell(具有包含连接到其stdin的Heredoc内容的临时文件),阅读cat的该副本的stdout,然后将该数据替换为传递给bash -c的单个参数。
在你的Go语言程序中,你没有交互式环境,所以你应该使用Go语言语法--而不是shell语法--来完成所有这些步骤,而且这些步骤在Go语言中是没有必要做的(不需要将数据文件写入临时文件,也不需要让/bin/cat读取该文件内容,没有必要让一个子进程运行命令替换来生成一个字符串--由这些内容组成--放在最终shell的命令行上),因此忽略所有这些步骤要明智得多。

jgwigjjp

jgwigjjp2#

我用printf测试了一下

pintf "#!/bin/bash\n\ntask:foo (){\n  echo \"test\"\n}\n"
-bash: !/bin/bash\n\ntask: event not found

这里解释了这种行为echo "#!" fails -- "event not found"
字符用于csh风格的历史扩展。您需要关闭此行为...
因此应该将set +o histexpand添加到.bash_profile

相关问题