Home
avatar

yuanjh

入门18_goCommand

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

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

不需返回值			bash 		csh
	运行命令 
	运行脚本 
需返回值
	运行命令 
	运行脚本 

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

	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,(比如文件不存在)

	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,(比如文件不存在)

	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,(比如文件不存在,脚本内容也不会执行)

	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采用下面方法是可行的。

	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")
	}

这样也行

	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,回显命令结果

	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

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

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

$ 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

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

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

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

 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并输入类似于以下内容的内容:

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

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

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

sudo visudo

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

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执行需要输入密码的命令

.zoukankan.com/mrylong-p-10763428.html
如何避免被sudo提示输入密码?:https://ubuntuqa.com/article/1342.html

入门