我正在尝试围绕 1Password CLI 可执行文件编写一个小型 Go 包装应用程序
op
,如下所示:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
op := exec.Command("op", "item", "list")
out, err := op.Output()
if e, ok := err.(*exec.ExitError); ok {
log.Fatal(e, ": ", string(e.Stderr))
}
fmt.Println(string(out))
}
但是,我不断收到以下错误:
2023/04/13 09:48:51 exit status 1: [ERROR] 2023/04/13 09:48:51 error initializing client: connecting to desktop app: read: connection reset, make sure the CLI is correctly installed and Connect with 1Password CLI is enabled in the 1Password app
但是当我从 Python 脚本中执行同样的操作时,如下所示:
#!/usr/bin/env python
import subprocess
subprocess.run(["op", "item", "list"])
...我得到的输出很好。
有趣的是,当我从 Go 应用程序调用 Python 脚本(名为
op.py
)时,它工作正常(修改后的 Go 应用程序如下所示):
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
op := exec.Command("/usr/bin/python3.11", "./op.py")
//op := exec.Command("op", "item", "list")
out, err := op.Output()
if e, ok := err.(*exec.ExitError); ok {
log.Fatal(e, ": ", string(e.Stderr))
}
fmt.Println(string(out))
}
我可以测试它是由 Go 应用程序打印的,而不是由 Python 脚本打印的,因为如果我删除 fmt.Printf(...)
,则不会打印任何内容。总结一下:
op
:不起作用
./op.py
) ->
op
:工作正常
./op.py
) ->
op
:工作正常
op
虚拟环境访问
pyenv
时遇到同样的问题。我认为问题在于 pyenv 的
python
可执行文件由用户(我)拥有。将所有权更改为root:Python解释器的root及其所在目录实际上有所帮助。不知道幕后发生了什么。 以下是步骤(我使用
--copies
创建虚拟环境,因此它不使用符号链接 - 因为符号链接会指向根拥有的文件):
$ python -m venv --copies .venv
$ .venv/bin/python -c "import subprocess; subprocess.call(['op', 'signin'])"
[ERROR] 2023/05/16 16:01:18 connecting to desktop app: read: connection reset, make sure the CLI is correctly installed and Connect with 1Password CLI is enabled in the 1Password app
# chown -R root:root .venv/
$ .venv/bin/python -c "import subprocess; subprocess.call(['op', 'signin'])"
Select account [Use arrows to move, type to filter]
底线:将生成 op
子进程的可执行文件(及其所在目录)的所有权更改为
root:root
另请参阅 1Password 上的此帖子,它看起来像是同一个问题:
https://1password.community/discussion/135768/calling-the-cli-from-a-go-program
1Password 应用程序版本 8.10.8 中得到修复。您现在应该能够运行脚本而无需任何解决方法。
func RunCmd(binary string, cmd []string) []string {
var output []string
c, b := exec.Command(binary, cmd...), new(bytes.Buffer)
c.Stdout = b
c.Run()
s := bufio.NewScanner(b)
for s.Scan() {
output = append(output, s.Text())
}
return output
}
此函数运行命令并将输出作为字符串切片返回(每行输出一个)。这是调用它的示例:
_ = RunCmd("./moveRobot", []string{"10.20.0.37", localIP.String(), "align"})
在您的示例中,您可以将其称为:
out = RunCmd("op", []string{"item", "get", "DocSend", "--format=json"})
您也可能需要提供 op
二进制文件的路径,所以我会尝试(如果它位于同一目录中,则使用
./op
这显然不会按原样返回 JSON 格式的输出,但您可以使用它作为起点,至少确认调用是否正常工作。