入门18_goCommand

执行脚本和命令(bash,csh)

go exec.Command()
01,bash支持~路径,csh(好像)不支持,所以测试时使用全路径
02,csh_script是csh脚本(#!/bin/csh开头),内容:chmod +x /home/e0004495/tmp/test_chmod

1
2
3
4
5
6
不需返回值			bash 		csh
运行命令
运行脚本
需返回值
运行命令
运行脚本

不需返回值:运行命令:bash,报错会反馈error,(比如文件不存在)

1
2
3
4
5
6
7
cmd := exec.Command("bash","-c","chmod +x /home/e0004495/tmp/test_chmod")
err := cmd.Run()
if err != nil {
fmt.Println("Execute Command failed:" + err.Error())
return
}
fmt.Println("Execute Command finished.")

不需返回值:运行命令:csh,报错会反馈error,(比如文件不存在)

1
2
3
4
5
6
7
cmd := exec.Command("csh","-c","chmod +x /home/e0004495/tmp/test_chmod")
err := cmd.Run()
if err != nil {
fmt.Println("Execute Command failed:" + err.Error())
return
}
fmt.Println("Execute Command finished.")

不需返回值:运行脚本:bash,报错会反馈error,(比如文件不存在)

1
2
3
4
5
6
7
8
9
in := bytes.NewBuffer(nil)
cmd := exec.Command("/bin/bash")
cmd.Stdin = in
in.WriteString("/home/e0004495/tmp/csh_script")
if err := cmd.Run();err != nil{
fmt.Printf("ERROR--> %s, msg:%s\n","/home/e0004495/tmp/csh_script",err.Error())
fmt.Println("xxxxx")
}
return

不需返回值:运行脚本:csh,报错不会反馈error,(比如文件不存在,脚本内容也不会执行)

1
2
3
4
5
6
7
8
9
in := bytes.NewBuffer(nil)
cmd := exec.Command("/bin/csh")
cmd.Stdin = in
in.WriteString("/home/e0004495/tmp/csh_script")
if err := cmd.Run();err != nil{
fmt.Printf("ERROR--> %s, msg:%s\n","/home/e0004495/tmp/csh_script",err.Error())
fmt.Println("xxxxx")
}
return

可见部分操作bash可行,而csh不行。并且也不会报错。给人感觉是被正确执行了
网上很多资料也都是针对bash的,直接迁移到csh就会有问题。
尤其注意,有些命令,不会报错,但实际脚本并不会被执行,这基本可以看做天坑了。很多研发并未养成测试错误case的习惯

csh采用下面方法是可行的。

1
2
3
4
5
cmd :=exec.Command("/home/e0004495/tmp/csh_script")
if err := cmd.Run();err != nil{
fmt.Printf("ERROR--> %s, msg:%s\n","~/tmp/csh_script",err.Error())
fmt.Println("xxxxx")
}

这样也行

1
2
3
4
5
cmd :=exec.Command("/bin/csh","/home/e0004495/tmp/csh_script")
if err := cmd.Run();err != nil{
fmt.Printf("ERROR--> %s, msg:%s\n","~/tmp/cs_script",err.Error())
fmt.Println("xxxxx")
}

脚本执行,csh,回显命令结果

1
2
3
4
5
6
7
cmd :=exec.Command("/bin/csh","/home/e0004495/tmp/csh_script")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run();err != nil {
fmt.Println(err)
}
fmt.Println("Result: " + out.String())

需要返回值的情况,用到再补充,放几篇文章
golang exec.Command 执行命令用法实例:https://blog.csdn.net/whatday/article/details/109277998

sudo密码输入问题

问题如下:使用go command执行shell命令,命令需要sudo权限,执行到此命令时,就会卡主
0412_01commandSudo

方法01

1
2
cmd := exec.Command("/bin/bash","-c","echo XXpasswordYY|sudo -S cat /etc/shadow")
或:sudo -S <<< "password" command

sudo指令有一个-S参数,这个参数表示sudo将从stdin接收密码,我们可以利用-S参数和Linux的pipe特性,来实现自动输入密码。

1
$ echo 'password' | sudo -S command

不是所有的程序都有类似于-S这样的参数。
自动输入密码有的时候很有用,比如你的cron里面的任务,很可能就需要这样的机制,但是也带来了安全方面的风险,需自行评估。
有些程序在于用户交互时,不能通过stdin接收交互信息,只能通过terminal device来接收,需要注意这个细节。

方法02

参考:go的ssh怎么执行sudo:https://golangtc.com/t/5397ef11320b5253b5000011
一个思路。主要就是连接一个ssh interactive shell ,然后连接stdin, stdout, stderr ,然后根据输出相应地处理就好了

方法03:gexpect

ThomasRooney/gexpect:https://github.com/ThomasRooney/gexpect/blob/master/examples/ftp.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
child, err := gexpect.Spawn("ftp ftp.openbsd.org")
if err != nil {
panic(err)
}
child.Expect("Name")
child.SendLine("anonymous")
child.Expect("Password")
child.SendLine("pexpect@sourceforge.net")
child.Expect("ftp> ")
child.SendLine("cd /pub/OpenBSD/3.7/packages/i386")
child.Expect("ftp> ")
child.SendLine("bin")
child.Expect("ftp> ")
child.SendLine("prompt")
child.Expect("ftp> ")
child.SendLine("pwd")
child.Expect("ftp> ")
log.Printf("Success\n")

方法04:其他不好使方法

貌似好使,实际不行的
Go 调用交互式shell:https://www.jianshu.com/p/9a1cbce39c3d

1
2
3
4
buffer := bytes.Buffer{}
buffer.Write([]byte("ZhangJian"))
cmd.Stdin = &buffer
err = cmd.Run()

如何在给定sudo密码的情况下使用Golang to shell命令:https://cloud.tencent.com/developer/ask/789868

1
2
cmd := exec.Command("sudo", "-S", "--", "cat", "/etc/shadow")
cmd.Stdin = strings.NewReader("mysecretpassword") // your password fed directly to sudo's stdin

在golang中输入命令的密码:https://www.5axxw.com/questions/content/5lsq1u

其他方式

特定命令免密码
echo ‘password’ | sudo -kS ls解决方案有效,但它有一些安全方面的缺点,其中大部分已在terdon的回答评论中提到过。因此,我想提出一个不同的方法:
如果它只是您经常需要执行的一个命令,例如apt-get upgrade,您可以配置您的系统,使sudo someCommand不需要密码。
为此,请运行visudo并输入类似于以下内容的内容:

1
myusername ALL=(ALL) NOPASSWD: /usr/bin/apt-get upgrade

请注意,如果输入不带参数的命令(例如,在此示例中没有upgrade),则允许用户使用任何参数运行它,因此请小心使用。

增加超时,而不是完全删除密码,因此您只需每天输入几次;在终端中,运行:

1
sudo visudo

在文件末尾,添加以下行以设置超时30分钟(用您的用户名替换jsmith)。

1
Defaults:jsmith timestamp_timeout=30

你可以使用你想要的任何数字; -1表示没有超时(仅在第一次提示时),而0表示即时超时(每次sudo时都会提示)。默认超时为5分钟。

最终选择:gexcept
需要注意的是:gexcept只能执行单个命令,不能通过”;,&&,||”等串联多个命令。建议每个命令循环即可

参考

sudo时,自动输入密码:https://www.maixj.net/ict/sudo-zidong-mima-21899
go的ssh怎么执行sudo:https://golangtc.com/t/5397ef11320b5253b5000011
GoLang执行需要输入密码的命令:t.zoukankan.com/mrylong-p-10763428.html
如何避免被sudo提示输入密码?:https://ubuntuqa.com/article/1342.html

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×