public static bool TryParse(string message, bool unescapePercents, out Command command)
        {
            command = null;
            if (string.IsNullOrEmpty(message))
            {
                return(false);
            }

            try
            {
                // Get the index of the prefix.
                int prefixIndex = message.IndexOf(LoggingCommandPrefix);
                if (prefixIndex < 0)
                {
                    return(false);
                }

                // Get the index of the separator between the command info and the data.
                int rbIndex = message.IndexOf(']', prefixIndex);
                if (rbIndex < 0)
                {
                    return(false);
                }

                // Get the command info (area.event and properties).
                int    cmdIndex = prefixIndex + LoggingCommandPrefix.Length;
                string cmdInfo  = message.Substring(cmdIndex, rbIndex - cmdIndex);

                // Get the command name (area.event).
                int    spaceIndex  = cmdInfo.IndexOf(' ');
                string commandName =
                    spaceIndex < 0
                    ? cmdInfo
                    : cmdInfo.Substring(0, spaceIndex);

                // Get the area and event.
                string[] areaEvent = commandName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                if (areaEvent.Length != 2)
                {
                    return(false);
                }

                string areaName  = areaEvent[0];
                string eventName = areaEvent[1];

                // Initialize the command.
                command = new Command(areaName, eventName);

                // Set the properties.
                if (spaceIndex > 0)
                {
                    string   propertiesStr   = cmdInfo.Substring(spaceIndex + 1);
                    string[] splitProperties = propertiesStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string propertyStr in splitProperties)
                    {
                        string[] pair = propertyStr.Split(new[] { '=' }, count: 2, options: StringSplitOptions.RemoveEmptyEntries);
                        if (pair.Length == 2)
                        {
                            command.Properties[pair[0]] = CommandStringConvertor.Unescape(pair[1], unescapePercents);
                        }
                    }
                }

                command.Data = CommandStringConvertor.Unescape(message.Substring(rbIndex + 1), unescapePercents);
                return(true);
            }
            catch
            {
                command = null;
                return(false);
            }
        }
        public void Execute(IExecutionContext context, Command command)
        {
            ArgUtil.NotNull(context, nameof(context));
            ArgUtil.NotNull(command, nameof(command));

            var eventProperties = command.Properties;
            var data            = command.Data;

            String name;

            if (!eventProperties.TryGetValue(TaskSetVariableEventProperties.Variable, out name) || String.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException(StringUtil.Loc("MissingVariableName"));
            }

            String  isSecretValue;
            Boolean isSecret = false;

            if (eventProperties.TryGetValue(TaskSetVariableEventProperties.IsSecret, out isSecretValue))
            {
                Boolean.TryParse(isSecretValue, out isSecret);
            }

            String  isOutputValue;
            Boolean isOutput = false;

            if (eventProperties.TryGetValue(TaskSetVariableEventProperties.IsOutput, out isOutputValue))
            {
                Boolean.TryParse(isOutputValue, out isOutput);
            }

            String  isReadOnlyValue;
            Boolean isReadOnly = false;

            if (eventProperties.TryGetValue(TaskSetVariableEventProperties.IsReadOnly, out isReadOnlyValue))
            {
                Boolean.TryParse(isReadOnlyValue, out isReadOnly);
            }

            if (context.Variables.IsReadOnly(name))
            {
                // Check FF. If it is on then throw, otherwise warn
                // TODO - remove this and just always throw once the feature has been fully rolled out.
                if (context.Variables.Read_Only_Variables)
                {
                    throw new InvalidOperationException(StringUtil.Loc("ReadOnlyVariable", name));
                }
                else
                {
                    context.Warning(StringUtil.Loc("ReadOnlyVariableWarning", name));
                }
            }

            if (isSecret)
            {
                if (!string.IsNullOrEmpty(data) &&
                    data.Contains(Environment.NewLine) &&
                    !AgentKnobs.AllowUnsafeMultilineSecret.GetValue(context).AsBoolean())
                {
                    throw new InvalidOperationException(StringUtil.Loc("MultilineSecret"));
                }

                var unescapePercents  = AgentKnobs.DecodePercents.GetValue(context).AsBoolean();
                var commandEscapeData = CommandStringConvertor.Escape(command.Data, unescapePercents);
                context.GetHostContext().SecretMasker.AddValue(commandEscapeData, WellKnownSecretAliases.TaskSetVariableCommand);
            }

            var checker = context.GetHostContext().GetService <ITaskRestrictionsChecker>();

            if (checker.CheckSettableVariable(context, name))
            {
                context.SetVariable(name, data, isSecret: isSecret, isOutput: isOutput, isReadOnly: isReadOnly);
            }
        }