为什么在调用go(golang)时,Powershell的Start-Process无法工作?

huangapple go评论125阅读模式
英文:

Why does Powershell Start-Process not work when called from go (golang)?

问题

我希望从Go中以不同的用户身份运行Windows批处理脚本,而不是运行Go程序的用户。运行Go的用户比应该运行批处理脚本的用户拥有更高的权限。

在Go中,有几种选项可以在Windows上以不同的用户身份执行进程,例如使用Go中的syscall包直接编写Windows调用。我尚未尝试过这种方法,但我已经尝试过使用PsExec和PowerShell。由于PowerShell已经作为标准安装在Windows 2008 R2上,所以我更倾向于使用PowerShell。

以下代码演示了我遇到的问题。在下面的演示中,我运行一个批处理脚本。该批处理脚本直接调用一个PowerShell脚本,然后从Go程序中调用它。结果是不同的。PowerShell脚本输出3个文件,但是从Go调用时,只输出2个文件。

为了完整起见,我还展示了如何创建用户。

C:\stackoverflow\demo.bat:

::::: 为演示创建一个新用户 :::::

:: 首先创建一个主目录
mkdir C:\Users\Tom
:: 删除Users组
icacls C:\Users\Tom /remove:g Users
:: 删除Everyone
icacls C:\Users\Tom /remove:g Everyone
:: 创建用户Tom并设置他的主目录
net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
:: 给Tom访问他的主目录的权限
icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
:: 给他远程桌面访问权限
net localgroup "Remote Desktop Users" /add Tom

::::: 现在直接调用PowerShell脚本 :::::

powershell -command C:\stackoverflow\demo.ps1
:: 显示创建的文件
dir C:\Users\Tom
:: 清理
del /f /q C:\Users\Tom\*

::::: 运行Go版本来做同样的事情 :::::

go run C:\stackoverflow\demo.go
:: 比较结果
dir C:\Users\Tom
:: 清理
del /f /s /q C:\Users\Tom
rmdir /s /q C:\Users\Tom
:: 删除用户
net user Tom /delete

C:\stackoverflow\demo.ps1

write-output "test output" | out-file C:\Users\Tom\started.txt
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @("Tom",(ConvertTo-SecureString -String "_Jerry123_" -AsPlainText -Force))
Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\stackoverflow -Credential ($credentials) -Wait
write-output "test output" | out-file C:\Users\Tom\finished.txt

C:\stackoverflow\demo.go

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    run(exec.Command("PowerShell", "-Command", "C:\\stackoverflow\\demo.ps1"))
}

func run(cmd *exec.Cmd) {
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    err := cmd.Start()
    if err != nil {
        panic(err)
    }
    err = cmd.Wait()
    if err != nil {
        panic(err)
    }
    fmt.Println("Done")
}

C:\stackoverflow\whoami.bat:

whoami > C:\Users\Tom\whoami.txt

现在,让我们看看结果-当从批处理脚本调用时,会创建started.txt、whoami.txt和finished.txt三个文件。当从Go调用时,只会创建started.txt和finished.txt。为什么会这样呢?

输出:

C:\stackoverflow>demo.bat

C:\stackoverflow>mkdir C:\Users\Tom

C:\stackoverflow>icacls C:\Users\Tom /remove:g Users
处理文件: C:\Users\Tom
成功处理了 1 个文件; 处理失败了 0 个文件

C:\stackoverflow>icacls C:\Users\Tom /remove:g Everyone
处理文件: C:\Users\Tom
成功处理了 1 个文件; 处理失败了 0 个文件

C:\stackoverflow>net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
命令成功完成。


C:\stackoverflow>icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
处理文件: C:\Users\Tom
成功处理了 1 个文件; 处理失败了 0 个文件

C:\stackoverflow>net localgroup "Remote Desktop Users" /add Tom
命令成功完成。


C:\stackoverflow>powershell -command C:\stackoverflow\demo.ps1

C:\stackoverflow>dir C:\Users\Tom
 卷 OSDisk 中的卷没有标签。
 卷的序列号是 CCD6-C9E7

 C:\Users\Tom 的目录

2015/06/18  06:36    <DIR>          .
2015/06/18  06:36    <DIR>          ..
2015/06/18  06:36                28 finished.txt
2015/06/18  06:36                28 started.txt
2015/06/18  06:36                55 whoami.txt
               3 个文件             111 字节
               2 个目录  16,489,889,792 可用字节

C:\stackoverflow>del /f /q C:\Users\Tom\*

C:\stackoverflow>go run C:\stackoverflow\demo.go
Done

C:\stackoverflow>dir C:\Users\Tom
 卷 OSDisk 中的卷没有标签。
 卷的序列号是 CCD6-C9E7

 C:\Users\Tom 的目录

2015/06/18  06:36    <DIR>          .
2015/06/18  06:36    <DIR>          ..
2015/06/18  06:36                28 finished.txt
2015/06/18  06:36                28 started.txt
               2 个文件              56 字节
               2 个目录  16,489,889,792 可用字节

C:\stackoverflow>del /f /s /q C:\Users\Tom
已删除文件 - C:\Users\Tom\finished.txt
已删除文件 - C:\Users\Tom\started.txt

C:\stackoverflow>rmdir /s /q C:\Users\Tom

C:\stackoverflow>net user Tom /delete
命令成功完成。


C:\stackoverflow>
英文:

I wish to run a Windows batch script from go, under a different user to the user running the go program. The user running go has more privileges than the user that should run the batch script.

From go there are several options for executing a process under a different user on Windows, such as writing windows calls directly using the syscall package in go. I have not attempted this yet, but I have tried both using PsExec and also Powershell. Powershell is preferred since it comes installed as standard on Windows 2008 R2.

The following code demonstrates the problem I have. In the following demo, I run a batch script. This batch script calls a Powershell script directly, and then calls it from a go program. The results are different. The Powershell script outputs 3 files, yet when called from go, only outputs 2 files.

For the sake of completeness, I also show how the user was created.

C:\stackoverflow\demo.bat:

::::: create a new user for the demo :::::

:: first create a home directory
mkdir C:\Users\Tom
:: remove Users group
icacls C:\Users\Tom /remove:g Users
:: remove Everyone
icacls C:\Users\Tom /remove:g Everyone
:: create user Tom and set his home directory
net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
:: Give Tom access to his home directory
icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
:: give him access to Remote Desktop
net localgroup &quot;Remote Desktop Users&quot; /add Tom

::::: now call powershell directly :::::

powershell -command C:\stackoverflow\demo.ps1
:: show which files were created
dir C:\Users\Tom
:: cleanup
del /f /q C:\Users\Tom\*

::::: run the go version to do the same thing :::::

go run C:\stackoverflow\demo.go
:: compare results
dir C:\Users\Tom
:: cleanup
del /f /s /q C:\Users\Tom
rmdir /s /q C:\Users\Tom
:: delete user
net user Tom /delete

C:\stackoverflow\demo.ps1

write-output &quot;test output&quot; | out-file C:\Users\Tom\started.txt
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @(&quot;Tom&quot;,(ConvertTo-SecureString -String &quot;_Jerry123_&quot; -AsPlainText -Force))
Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\stackoverflow -Credential ($credentials) -Wait
write-output &quot;test output&quot; | out-file C:\Users\Tom\finished.txt

C:\stackoverflow\demo.go

package main

import (
    &quot;fmt&quot;
    &quot;os&quot;
    &quot;os/exec&quot;
)

func main() {
    run(exec.Command(&quot;PowerShell&quot;, &quot;-Command&quot;, &quot;C:\\stackoverflow\\demo.ps1&quot;))
}

func run(cmd *exec.Cmd) {
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    err := cmd.Start()
    if err != nil {
        panic(err)
    }
    err = cmd.Wait()
    if err != nil {
        panic(err)
    }
    fmt.Println(&quot;Done&quot;)
}

C:\stackoverflow\whoami.bat:

whoami &gt; C:\Users\Tom\whoami.txt

And now, the results - here you see when called from batch script, files started.txt, whoami.txt, finished.txt all get created. When called from go, only started.txt and finished.txt get created. Why is that?

Output:

C:\stackoverflow&gt;demo.bat

C:\stackoverflow&gt;mkdir C:\Users\Tom

C:\stackoverflow&gt;icacls C:\Users\Tom /remove:g Users
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow&gt;icacls C:\Users\Tom /remove:g Everyone
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow&gt;net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
The command completed successfully.


C:\stackoverflow&gt;icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow&gt;net localgroup &quot;Remote Desktop Users&quot; /add Tom
The command completed successfully.


C:\stackoverflow&gt;powershell -command C:\stackoverflow\demo.ps1

C:\stackoverflow&gt;dir C:\Users\Tom
 Volume in drive C is OSDisk
 Volume Serial Number is CCD6-C9E7

 Directory of C:\Users\Tom

06/18/2015  06:36 AM    &lt;DIR&gt;          .
06/18/2015  06:36 AM    &lt;DIR&gt;          ..
06/18/2015  06:36 AM                28 finished.txt
06/18/2015  06:36 AM                28 started.txt
06/18/2015  06:36 AM                55 whoami.txt
               3 File(s)            111 bytes
               2 Dir(s)  16,489,889,792 bytes free

C:\stackoverflow&gt;del /f /q C:\Users\Tom\*

C:\stackoverflow&gt;go run C:\stackoverflow\demo.go
Done

C:\stackoverflow&gt;dir C:\Users\Tom
 Volume in drive C is OSDisk
 Volume Serial Number is CCD6-C9E7

 Directory of C:\Users\Tom

06/18/2015  06:36 AM    &lt;DIR&gt;          .
06/18/2015  06:36 AM    &lt;DIR&gt;          ..
06/18/2015  06:36 AM                28 finished.txt
06/18/2015  06:36 AM                28 started.txt
               2 File(s)             56 bytes
               2 Dir(s)  16,489,889,792 bytes free

C:\stackoverflow&gt;del /f /s /q C:\Users\Tom
Deleted file - C:\Users\Tom\finished.txt
Deleted file - C:\Users\Tom\started.txt

C:\stackoverflow&gt;rmdir /s /q C:\Users\Tom

C:\stackoverflow&gt;net user Tom /delete
The command completed successfully.


C:\stackoverflow&gt;

答案1

得分: 1

已翻译的内容:

修复了。

  1. 默认情况下,用户Tom无法访问C:\Stackoverflow文件夹,为了使任何东西都能运行,我不得不给予Everyone在该文件夹中读取/执行项目的权限,否则start-process将失败。

  2. 将profilepath添加到net user命令中。这样可以防止我在评论中提到的额外文件夹被创建:

    net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /profilepath:C:\Users\Tom /y

  3. Start-Process行替换为:

    Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\Users\Tom -Credential ($credentials) -Wait

为什么?

将工作目录设置为C:\StackOverflow后,执行批处理文件的命令解释器会在内置命令之前找到whoami.bat,从而导致各种问题。一旦我将工作目录移动到Tom的文件夹中,一切都按照您的要求正常工作。

替代方案:

不要将批处理文件命名为与内置命令相同的名称。

英文:

Fixed it.

  1. The user Tom cannot access the C:\Stackoverflow folder by default, in order for anything to run, I had to give Everyone access to Read/Execute items in that folder, else the start-process failed miserably

  2. Add profilepath to the net user command. This prevented the extra folders being created that I mentioned in a comment:

    net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /profilepath:C:\Users\Tom /y

  3. Replace the Start-Process line with:

    Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\Users\Tom -Credential ($credentials) -Wait

Why?

With the working directory set to C:\StackOverflow, the command interpreter that is executing the batch file finds the whoami.bat before the builtin, and all kinds of broken happens. Once I moved the working directory to Tom's folder, everything worked exactly as you wanted.

Alternative:

Don't name your batch files the same as builtin commands.

huangapple
  • 本文由 发表于 2015年6月18日 21:44:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/30917132.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定