【问题标题】:Unknown error - cmdlet Invoke-SCScript未知错误 - cmdlet Invoke-SCScript
【发布时间】:2015-01-13 11:32:23
【问题描述】:

这就是我所拥有的(完整的插件代码):

using Microsoft.SystemCenter.VirtualMachineManager;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns.ContextTypes;
using Microsoft.VirtualManager.Remoting;
using System;
using System.AddIn;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Forms;

namespace Microsoft.VirtualManager.UI.AddIns.BackupAddIn
{
[AddIn("Make Backup")]
public class BackupAddIn : ActionAddInBase
{
    protected const string PowershellPath = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe";
    protected const string DefaultDirectory = "C:\\ClusterStorage\\Volume2";
    protected const string WildcardVMName = "{{VMName}}";
    protected const string WildcardError = "{{Error}}";
    protected const string BackupDirectoryBase = "{{Base}}";
    protected const string BackupDirectoryName = "{{Name}}";
    protected const string BackupDirectoryDate = "{{Date}}";

    public enum JobState { Initialized, Success, Fail };

    protected const string BackupDirectoryTemplate = BackupDirectoryBase + "\\" + BackupDirectoryName + "\\" + BackupDirectoryDate + "\\";
    protected static readonly ReadOnlyCollection<string> AllowedBackupStates = new ReadOnlyCollection<string>(new string[] { "PowerOff", "Paused"/*, "Saved"*/});

    public override bool CheckIfEnabledFor(IList<ContextObject> contextObjects)
    {
        if (contextObjects != null && contextObjects.Count > 0)
        {
            foreach (var host in contextObjects.OfType<HostContext>())
            {
                if (host.ComputerState != ComputerState.Responding)
                {
                    return false;
                }
            }
            return true;
        }

        return false;
    }

    public override void PerformAction(IList<ContextObject> contextObjects)
    {
        if (contextObjects != null)
        {
            // check if we have VMs selected
            var VMs = contextObjects.OfType<VMContext>();
            if (VMs != null && VMs.Count() > 0)
            {
                // check if VMs are in a good state
                var badVMs = VMs.Where(vm => AllowedBackupStates.Contains(vm.Status.ToString()) == false).ToArray();
                if (badVMs != null && badVMs.Length > 0)
                {
                    MessageBox.Show("Backup not possible!\r\nThe following VMs are still running:\r\n\r\n" + string.Join(", ", badVMs.Select(vm => vm.Name)));
                }
                else
                {
                    // ask for backup directory
                    string backupDir = Microsoft.VisualBasic.Interaction.InputBox("Enter a path on the host to export the selected virtual machine(s) to.", "Export path", DefaultDirectory);
                    if (string.IsNullOrEmpty(backupDir) == false)
                    {
                        if (backupDir.EndsWith("\\"))
                        {
                            backupDir = backupDir.Substring(0, backupDir.Length - 1);
                        }

                        // go
                        /*foreach (var vm in VMs)
                        {
                            exportVM(vm, backupDir);
                        }*/

                        // testing to export multiple vms in one invoke
                        exportVMs(VMs, backupDir);
                    }
                }
            }
        }
    }

    public string getDate()
    {
        var date = DateTime.Now;
        return date.Year.ToString()
            + (date.Month < 10 ? "0" : "") + date.Month.ToString()
            + (date.Day < 10 ? "0" : "") + date.Day.ToString()
            + "_"
            + (date.Hour < 10 ? "0" : "") + date.Hour.ToString()
            + (date.Minute < 10 ? "0" : "") + date.Minute.ToString();
    }

    public void ManageJob(string name, JobState state, string message = null)
    {
        string command;
        if (state == JobState.Initialized)
        {
            command = string.Format("New-SCExternalJob -Name \"{0}\"", name);
        }
        else if (state == JobState.Success)
        {
            command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Complete -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup successfully started." : message.Replace("\"", "'")) + "\"", name);
        }
        else
        {
            command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Failed -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup FAILED." : message.Replace("\"", "'")) + "\"", name);
        }

        //MessageBox.Show(command);

        PowerShellContext.ExecuteScript<Host>(
            command,
            (profiles, error) =>
            {
                if (error != null)
                {
                    MessageBox.Show("Cannot modify job state\r\nError: " + error.Problem);
                }
            }
        );
    }

    public void exportVMs(IEnumerable<VMContext> VMs, string backupDir)
    {
        string date = getDate();
        string VMS = "";
        string fullBackupDirS = BackupDirectoryTemplate.Replace(BackupDirectoryBase, backupDir).Replace(BackupDirectoryName, "_VMBackups").Replace(BackupDirectoryDate, date);
        VMS = "'" + string.Join("', '", VMs.Select(vm => vm.Name).ToArray()) + "'";
        string command = string.Format("Export-VM -Name {0} -Path '{1}'", VMS, fullBackupDirS);
        MessageBox.Show(command);

        // We need to manager jobs in another thread probably --------------------------------------------------------------!!!
        string jobname = "Starting_backup_of_multiple_machines";

        mkShortcuts(backupDir, date, VMs.Select(vm => vm.Name).ToArray(), VMs.First());
        //!            execPSScript(jobname, scvmmPsCommand(command, VMs.First()), VMs.First(), WildcardVMName + ": Backup successful.", WildcardVMName + ": Backup FAILED!\r\nError: " + WildcardError, backupDir, date, VMs.Select(vm => vm.Name).ToArray());
    }

    public String scvmmPsCommand(string command, VMContext vm, string appPath = PowershellPath)
    {
        return string.Format("Invoke-SCScriptCommand -Executable {0} -VMHost (Get-SCVMHost -ID \"{1}\") -CommandParameters \"{2}\" -RunAsynchronous -TimeoutSeconds 360000", appPath, vm.VMHostId.ToString(), command);
    }

    // Make a shortcut from the machines backup directory to the backup in the "_VMBackups"-folder
    public void mkShortcuts(string path, string date, string[] names, VMContext vm)
    {
        string command = "$shell = New-Object -ComObject WScript.Shell;";
        foreach (var n in names)
        {
            command = command + string.Format(" $shortc = $shell.CreateShortcut('{0}\\{1}\\{2}.lnk'); $shortc.TargetPath = '{0}\\_VMBackup\\{2}\\{1}'; $shortc.Save();", path, n, date);
        }
        string fullCommand = scvmmPsCommand(command, vm);
        MessageBox.Show(fullCommand);
        execPSScript("Create_ShortcutS", fullCommand, vm, "Shortcut(s) created.", "FAILED to create Shortcut(s)!");
    }

    public void execPSScript(string jobname, string command, VMContext vm, string successMessage, string errorMessage, string path = "", string date = "", string[] names = null)
    {
        ManageJob(jobname, JobState.Initialized);
        PowerShellContext.ExecuteScript<Host>(
            command,
            (vms, error) =>
            {
                if (error != null)
                {
                    ManageJob(jobname, JobState.Fail, errorMessage.Replace(WildcardVMName, vm.Name).Replace(WildcardError, error.Problem));
                }
                else
                {
                    ManageJob(jobname, JobState.Success, successMessage.Replace(WildcardVMName, vm.Name));
                    if (string.IsNullOrEmpty(path) == false)
                    {
                        //mkShortcuts(path, date, names, vm);
                    }
                }
            }
        );
    }
}
}

当我运行插件时,我收到一个错误框,上面写着:“未知脚本错误。表达式未关闭 - “)”丢失。

+ ... andard'; .Save();
+                    ~
An expression was expected after '('.
+ CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordEx 
   ception
+ FullyQualifiedErrorId : ExpectedExpression". Weitere Informationen finden Sie im Standardfehlerprotokoll "C:\Windows\TEMP\gce_stderrord07b04547c74493caa6bdba9087df444.log".

但是我在我的代码中找不到错误,因为它在手动输入主机上的 powershell 并且 .Save() 不带任何参数时起作用。你有什么想法吗?

【问题讨论】:

  • 什么,究竟 错误消息说明了什么?只说 "error Box that say sth like" 没有 有帮助。错误消息通常包含对触发错误的行号的引用。
  • “错误:运行 PS 脚本时出现未知错误:关闭表达式“)”丢失”(德语翻译)这就是它所说的一切
  • 错误提示您缺少括号,然后您缺少括号。或逗号,或引号,或其他任何东西。这是 PowerShell 吗?你在用什么扩展?你的代码应该做什么?好的,它是 C# 调用 PowerShell 调用 vb 脚本,但为什么呢?
  • 如果错误消息没有帮助,那么您必须将所有脚本命令写入控制台窗口或文件而不是执行它们。复制和粘贴(不要键入,以便避免意外修复错误)到 Powershell 提示符以查看哪个命令失败。
  • 我知道这很愚蠢,但我的工作也是如此……这是 SCVMM 插件的 C# 代码,它在主机上创建了我的虚拟机的备份。现在我想在主机上创建一个到备份目录的快捷方式。因此,我在插件调用的系统中心上的 ps 脚本上调用 ps 脚本来执行 vb 函数。我知道这很愚蠢,我无能为力。 ://

标签: c# powershell wsh cmdlet


【解决方案1】:

好的,而不是

command2 = command2 + string.Format(" ${1} = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); ${1}.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; ${1}.Save();", backupDir, vm.Name, date);

试试

command2 = command2 + string.Format(" $shortc = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); $shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; $shortc.Save();", backupDir, vm.Name, date);
command2 = "'" + command2 + "'"

这不起作用,很多其他事情也没有。 基本上,问题在于命令字符串传递给 PowerShell 执行的方式。 PowerShell 将检查刺痛并尝试枚举所有变量。这不是期望的行为。试图将整个字符串括在单引号中,但这没有帮助。 最后的解决方案是使用 PowerShell 转义字符“`”来屏蔽所有变量名。

command2 = command2 + string.Format(" `$shortc = `$shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); `$shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; `$shortc.Save();", backupDir, vm.Name, date);

【讨论】:

  • 虽然重用一个已经声明的变量是个好主意..它并没有解决我最初的问题。意外的标记 ”)”。为什么?啊!!
  • 正如我所说,在黑暗中刺伤,抱歉...没有足够的信息来开始故障排除。你能得到你的 command2 变量的内容吗,或者如果代码根本不会执行,发布整个代码?
  • 谢谢。因此,首先,您的代码会生成适当的 PowerShell 命令来创建字符串。所以我和你一样难过。然后我注意到了你的错误。 “......和德'; .Save();”这看起来像是您的 PS 命令“{1}\”的结尾; $shortc.Save();" 变量为空白。我之前已经看到过,PowerShell 将在作为字符串传入的命令中用值(空白)替换变量。尝试在执行周围添加另一组单引号声明。我已经更新了上面的答案。
  • 对不起 - 也没有帮助。他们仍然是空白的,现在它抱怨第一个字符'。 >:[ 我的猜测是,由于这些是对象,powershell 无法在错误消息中将它们显示为字符串,但我不知道这是否属实。我猜还是和过去几千小时一样的问题。 ^~^
  • 要尝试的另一件事是在每个变量名称之前添加 PowerShell 转义字符。所以 PS escape ' 就在每个 $...
猜你喜欢
  • 2018-07-15
  • 2012-09-18
  • 2016-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
相关资源
最近更新 更多