【问题标题】:How do you execute an arbitrary native command from a string?如何从字符串执行任意本机命令?
【发布时间】:2023-04-01 12:04:01
【问题描述】:

我可以通过以下场景表达我的需求:编写一个函数,该函数接受要作为本机命令运行的字符串。

这个想法并不太牵强:如果您正在与公司其他地方的其他命令行实用程序交互,这些实用程序为您提供逐字运行的命令。因为您不控制命令,所以您需要接受任何有效命令作为输入。这些是我无法轻易克服的主要问题:

  1. 该命令可能会执行一个位于包含空格的路径中的程序:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
    
  2. 该命令可能包含带有空格的参数:

    $command = 'echo "hello world!"';
    
  3. 命令可能有单引号和双引号:

    $command = "echo `"it`'s`"";
    

有没有任何干净的方法来完成这个?我只能设计出奢华而丑陋的解决方法,但对于脚本语言,我觉得这应该非常简单。

【问题讨论】:

    标签: scripting powershell


    【解决方案1】:

    Invoke-Expression,别名为iex。以下将适用于您的示例 #2 和 #3:

    iex $command
    

    某些字符串不会按原样运行,例如您的示例 #1,因为 exe 在引号中。这将按原样工作,因为字符串的内容正是您从 Powershell 命令提示符直接运行它的方式:

    $command = 'C:\somepath\someexe.exe somearg'
    iex $command
    

    但是,如果 exe 包含在引号中,则需要 & 的帮助才能使其运行,如本例所示,从命令行运行:

    >> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"
    

    然后在脚本中:

    $command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
    iex "& $command"
    

    您可以通过检测命令字符串的第一个字符是否为" 来处理几乎所有情况,就像在这个简单的实现中一样:

    function myeval($command) {
        if ($command[0] -eq '"') { iex "& $command" }
        else { iex $command }
    }
    

    但是您可能会发现必须以不同的方式调用其他一些情况。在这种情况下,您可能需要使用try{}catch{},可能用于特定的异常类型/消息,或者检查命令字符串。

    如果您总是收到绝对路径而不是相对路径,那么除了上述 2 之外,您不应该有很多特殊情况(如果有的话)。

    【讨论】:

    • 别名很棒。请记住,如果您移动到另一台机器或将该脚本发送给其他人,则可能不会设置别名。首选 PowerShell 函数的全名。
    • @Doug:大多数时候我这样做或使用内置别名(尤其是为了在命令行上简洁)。 eval 是半开玩笑的,因为在许多其他脚本语言中都是这样称呼的,这不是我看到的第一个问题,有人不知道 invoke-expression。而且 OP 的案例听起来像是内部脚本而已。
    • 我试过这个,但它不适用于:$command = '"C:\Program Files\Windows Media Player\mplayer2.exe" "H:\Audio\Music\Stevie Wonder \Stevie Wonder - Superstition.mp3"'
    • 您可能需要输入“&”或“.”如果它不是 powershell-native,则在实际命令之前签名,例如Invoke-Expression "& $command"
    • 我无法按照我需要的方式为我工作。我最终使用了 & cmd /c $command,它不需要修改 $command 中的引号
    【解决方案2】:

    还请参阅此 Microsoft Connect 报告,从本质上讲,使用 PowerShell 运行 shell 命令是多么的困难(哦,具有讽刺意味)。

    http://connect.microsoft.com/PowerShell/feedback/details/376207/

    他们建议使用--% 来强制 PowerShell 停止尝试向右解释文本。

    例如:

    MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj
    

    【讨论】:

    【解决方案3】:

    在尝试解析注册表以查找卸载字符串并执行它们时,接受的答案对我不起作用。原来我根本不需要打电话给Invoke-Expression

    我终于遇到了这个nice template 来查看如何执行卸载字符串:

    $path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
    $app = 'MyApp'
    $apps= @{}
    Get-ChildItem $path | 
        Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} | 
        ForEach-Object -process {$apps.Set_Item(
            $_.getvalue('UninstallString'),
            $_.getvalue('DisplayName'))
        }
    
    foreach ($uninstall_string in $apps.GetEnumerator()) {
        $uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
        & $uninstall_app $uninstall_arg
    }
    

    这对我有用,即因为$app 是一个内部应用程序,我知道它只有两个参数。对于更复杂的卸载字符串,您可能需要使用join operator。另外,我只是使用了哈希映射,但实际上,您可能想要使用数组。

    另外,如果您确实安装了同一个应用程序的多个版本,此卸载程序将一次循环浏览它们,这让MsiExec.exe 感到困惑,所以也有。

    【讨论】:

      【解决方案4】:

      如果要使用调用运算符,参数可以是存储在变量中的数组:

      $prog = 'c:\windows\system32\cmd.exe'
      $myargs = '/c','dir','/x'
      & $prog $myargs
      

      调用运算符也适用于 ApplicationInfo 对象。

      $prog = get-command cmd
      $myargs = -split '/c dir /x'
      & $prog $myargs
      

      【讨论】:

      • @YoraiLevi 参数模式 vs 表达式模式 & $wsl (-split "--install -d Ubuntu-20.04")
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-20
      • 2011-09-12
      • 2011-09-19
      • 2015-11-08
      相关资源
      最近更新 更多