elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Introducción a Git (Primera Parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Pasar comando desde CMD a aplicación de consola C#
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Pasar comando desde CMD a aplicación de consola C#  (Leído 7,127 veces)
HCK.

Desconectado Desconectado

Mensajes: 181



Ver Perfil
Pasar comando desde CMD a aplicación de consola C#
« en: 8 Diciembre 2015, 19:51 pm »

Hola chic@s, llevo buscando una duda por internet desde hace tiempo, y tras no encontrar información (tal vez por no saber buscarlo con el tecnicismo exacto, o porque apenas hay información sobre mi duda...) recurro a vosotros haber si me podeis hechar una mano.  :)

Quería saber si es posible hacer interactuar a una aplicación de consola programada en C# con el "símbolo de sistema"... Es decir, por ejemplo:

Desde la consola de Windows llamo a mi aplicación con un comando en concreto que tenga programado y esperar respuesta desde la misma shell de Windows....
Código:
miprograma.exe --show "Hello World"

Y que desde la misma consola de Windows, salga la respuesta programada a ese comando de la aplicación de C#.

Gracias  :P, un saludo.


En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.866



Ver Perfil
Re: Pasar comando desde CMD a aplicación de consola C#
« Respuesta #1 en: 8 Diciembre 2015, 21:23 pm »

Para obtener los argumentos que se han pasado por linea de comandos, puedes utilizar el siguiente método:

Para "enviar una respuesta" lo harías escribiendo en el buffer de la consola, con el siguiente método:
(o Console.Write)

Al finalizar la ejecución de la aplicación no olvides enviar un código de salida apropiado utilizando el siguiente método:



Para parsear la sintaxis de los argumento en cuestión eso siempre es trabajo del programador, mediante la manipulación de string (un split, un substring, etc).

Citar
Código:
miprograma.exe --show "Hello World"

En ese ejemplo que has mostrado tienes que comprobar que el primer argumento sea un identificador (--cosa) y que exista un string adicional, y que ese string adicional no sea otro identificador sino el valor del parámetro que esperas (ya que el usuario se puede equivocar, y eso requiere un control de errores);
Yo te sugiero utilizar una sintaxis mucho más cómoda y apropiada para el parsing de argumentos command-line (tomando como ejemplo el diseño de algunas aplicaciones de la SDK de Microsoft):
Código:
Aplicación.exe /Switch="Valor"
o si lo prefieres así...:
Código:
Aplicación.exe --Switch="Valor"

De esa manera lo tienes todo en un único string o argumento facilmente delimitable mediante el símbolo "=", evitando la necesidad de parsear/evaluar un argumento adicional.

Saludos.


« Última modificación: 8 Diciembre 2015, 21:28 pm por Eleкtro » En línea



HCK.

Desconectado Desconectado

Mensajes: 181



Ver Perfil
Re: Pasar comando desde CMD a aplicación de consola C#
« Respuesta #2 en: 8 Diciembre 2015, 22:19 pm »

 :D HoW, eres un máquina. La verdad que no sabia ni por donde empezar, y a pesar de buscarlo con mis palabras, no conseguia encontrar nada. :D :D :D

Muchísimas gracias Elektro :), por cierto, felicitarte por la API que has publicado hace poco, muy buena, y por lo del magnetismo para las ventanas.

Un saludo crack!
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.866



Ver Perfil
Re: Pasar comando desde CMD a aplicación de consola C#
« Respuesta #3 en: 9 Diciembre 2015, 10:12 am »

La verdad que no sabia ni por donde empezar, y a pesar de buscarlo con mis palabras, no conseguia encontrar nada. :D :D :D

Desde mi ignorancia me atrevo a decir que probablemente no encuentras información al respecto por que la intentas buscar en Español.
( excepto si utilizases el buscador de la MSDN en Español, claro está. )

Digo yo que una simple búsqueda en Google por palabras clave como "C# read console arguments" te habría sobrado para salir de dudas, y como ves usando términos que ya conocías.



Muchísimas gracias Elektro :), por cierto, felicitarte por la API que has publicado hace poco, muy buena, y por lo del magnetismo para las ventanas.

Gracias a ti por el comentario.

Venga, te lo has ganado, no quería ponertelo del todo facl en la parte más importante pero por si te sirve de algo te mostraré como suelo parsear los argumentos (argumentos que sigan las reglas de sintaxis que te comenté en el otro comentario. )

Primero te comentaré la manera más facil (o vaga); lo que yo suelo hacer es declarar una variable por cada argumento en el código fuente (ya que se supone que cada argumento que el usuario pase debe corresponder a un objeto/variable cuyo valor sea asignado en el código fuente),
y entonces con este método parseo los argumentos y le asigno los valores a esas variables:

( Esto es un código traducido al vuelo de Vb.Net a C# )

Código
  1. private string variable;
  2.  
  3. private void ParseArguments(IEnumerable<string> args = null) {
  4.  
  5. foreach (string arg in args) {
  6. switch (true) {
  7. case arg.StartsWith("/?"):
  8.                                // Print Help.
  9. break;
  10.  
  11. case arg.StartsWith("/Switch1=", StringComparison.OrdinalIgnoreCase):
  12. variable = arg.Substring(arg.IndexOf("=") + 1);
  13. break;
  14. }
  15.  
  16. }
  17. }
  18.  
  19. //=======================================================
  20. //Service provided by Telerik (www.telerik.com)
  21. //=======================================================

Es una solución simple pero efectiva hasta cierto punto ya que no hay controles de errores, pero siempre puedes adaptarlo para que sea tan sofisticado que quieras que sea.

Saludos!
En línea



Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.866



Ver Perfil
Re: Pasar comando desde CMD a aplicación de consola C#
« Respuesta #4 en: 9 Diciembre 2015, 10:23 am »

Ahora vamos con la manera tediosa, pero más eficiente y sobre todo organizada.

Primero de todo, creamos un Type para representar un parámetro command-line.

Lo que deberiamos tener en cuenta en el diseño del modelo es al menos lo siguiente:
  • El nombre del parámetro
  • El símbolo que separa al nombre del parámetro del valor que indique el usuario en el argumento.
  • El valor por defecto del parámetro (para parámetros que son opcionales, o también para argumentos vacíos. )
  • El valor que le da el usuario (valor que parsearemos en los argumentos de la aplicación)
  • Un flag que indique si este parámetro es opcional o no. Si no es opcional entonces deberiamos mostrar un error cuando el usuario no usa este parámetro.

( A la lista que cada cual le añada otros factores que sean importantes para sus necesidades, como pro ejemplo el orden en el que se reciben los parámetros en los argumentos. )

Ahí va (lo he convertido a C# pero no he testeado la sintaxis) :
Código
  1. // ***********************************************************************
  2. // Author   : Elektro
  3. // Modified : 09-December-2015
  4. // ***********************************************************************
  5.  
  6. using Microsoft.VisualBasic;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Data;
  11. using System.Diagnostics;
  12.  
  13. namespace Types
  14. {
  15.  
  16. #region " Commandline Parameter (Of T) "
  17.  
  18. /// ----------------------------------------------------------------------------------------------------
  19. /// <summary>
  20. /// Represents a Commandline Parameter.
  21. /// </summary>
  22. /// ----------------------------------------------------------------------------------------------------
  23. /// <typeparam name="T">
  24. /// The type of value that the parameter takes.
  25. /// </typeparam>
  26. /// ----------------------------------------------------------------------------------------------------
  27. public class CommandlineParameter<T>
  28. {
  29.  
  30. #region " Properties "
  31.  
  32. /// ----------------------------------------------------------------------------------------------------
  33. /// <summary>
  34. /// Gets or sets a value indicating whether this parameter is required for the application.
  35. /// <para></para>
  36. /// A value of <see langword="true"/> means the user needs to assign a value for this parameter.
  37. /// <para></para>
  38. /// A value of <see langword="false"/> means this is an optional parameter so no matter if the user sets a custom value.
  39. /// </summary>
  40. /// ----------------------------------------------------------------------------------------------------
  41. /// <value>
  42. /// <see langword="true"/> if this parameter is required for the application; otherwise, <see langword="false"/>.
  43. /// </value>
  44. /// ----------------------------------------------------------------------------------------------------
  45. public bool IsRequired { get; set; }
  46.  
  47. /// ----------------------------------------------------------------------------------------------------
  48. /// <summary>
  49. /// Gets or sets the parameter name.
  50. /// </summary>
  51. /// ----------------------------------------------------------------------------------------------------
  52. /// <value>
  53. /// The parameter name.
  54. /// </value>
  55. /// ----------------------------------------------------------------------------------------------------
  56. public string Name {
  57. [DebuggerStepThrough()]
  58. get { return this.nameB; }
  59. [DebuggerStepThrough()]
  60. set { this.EvaluateName(value); }
  61. }
  62. /// ----------------------------------------------------------------------------------------------------
  63. /// <summary>
  64. /// ( Backing Field )
  65. /// The parameter name.
  66. /// </summary>
  67. /// ----------------------------------------------------------------------------------------------------
  68. private string nameB;
  69.  
  70. /// ----------------------------------------------------------------------------------------------------
  71. /// <summary>
  72. /// Gets or sets the parameter separator.
  73. /// <para></para>
  74. /// This character separates the parameter from the value in the argument.
  75. /// </summary>
  76. /// ----------------------------------------------------------------------------------------------------
  77. /// <value>
  78. /// The parameter separator.
  79. /// </value>
  80. /// ----------------------------------------------------------------------------------------------------
  81. public char Separator {
  82. [DebuggerStepThrough()]
  83. get { return this.separatorB; }
  84. [DebuggerStepThrough()]
  85. set { this.EvaluateSeparator(value); }
  86. }
  87. /// ----------------------------------------------------------------------------------------------------
  88. /// <summary>
  89. /// ( Backing Field )
  90. /// The parameter separator.
  91. /// </summary>
  92. /// ----------------------------------------------------------------------------------------------------
  93. private char separatorB;
  94.  
  95. /// ----------------------------------------------------------------------------------------------------
  96. /// <summary>
  97. /// Gets or sets the parameter value.
  98. /// <para></para>
  99. /// This value should be initially <see langword="Nothing"/> before parsing the commandline arguments of the application;
  100. /// <para></para>
  101. /// the value of the parameter should be assigned by the end-user when passing an argument to the application.
  102. /// <para></para>
  103. /// To set a default value for this parameter, use <see cref="CommandlineParameter(Of T).DefaultValue"/> property instead.
  104. /// </summary>
  105. /// ----------------------------------------------------------------------------------------------------
  106. /// <value>
  107. /// The parameter value.
  108. /// </value>
  109. /// ----------------------------------------------------------------------------------------------------
  110. public T Value { get; set; }
  111.  
  112. /// ----------------------------------------------------------------------------------------------------
  113. /// <summary>
  114. /// Gets or sets the default parameter value.
  115. /// <para></para>
  116. /// This value should be take into account if, after parsing the commandline arguments of the application,
  117. /// <see cref="CommandlineParameter(Of T).Value"/> is <see langword="Nothing"/>,
  118. /// meaning that the end-user didn't assigned any custom value to this parameter.
  119. /// </summary>
  120. /// ----------------------------------------------------------------------------------------------------
  121. /// <value>
  122. /// The default parameter value.
  123. /// </value>
  124. /// ----------------------------------------------------------------------------------------------------
  125. public T DefaultValue { get; set; }
  126.  
  127. #endregion
  128.  
  129. #region " Operator Overloading "
  130.  
  131. /// ----------------------------------------------------------------------------------------------------
  132. /// <summary>
  133. /// Performs an implicit conversion from <see cref="CommandlineParameter(Of T)"/> to <see cref="CommandlineParameter"/>.
  134. /// </summary>
  135. /// ----------------------------------------------------------------------------------------------------
  136. /// <param name="param">
  137. /// The <see cref="CommandlineParameter(Of T)"/> to convert.
  138. /// </param>
  139. /// ----------------------------------------------------------------------------------------------------
  140. /// <returns>
  141. /// The result of the conversion.
  142. /// </returns>
  143. /// ----------------------------------------------------------------------------------------------------
  144. public static implicit operator CommandlineParameter(CommandlineParameter<T> param)
  145. {
  146.  
  147. return new CommandlineParameter {
  148. Name = param.Name,
  149. Separator = param.Separator,
  150. DefaultValue = param.DefaultValue,
  151. Value = param.Value,
  152. IsRequired = param.IsRequired
  153. };
  154.  
  155. }
  156.  
  157. #endregion
  158.  
  159. #region " Private Methods "
  160.  
  161. /// ----------------------------------------------------------------------------------------------------
  162. /// <summary>
  163. /// Evaluates an attempt to assign the parameter name.
  164. /// </summary>
  165. /// ----------------------------------------------------------------------------------------------------
  166. /// <param name="name">
  167. /// The parameter name.
  168. /// </param>
  169. /// ----------------------------------------------------------------------------------------------------
  170. /// <exception cref="ArgumentException">
  171. /// The parameter name cannot contain the separator character.;name
  172. /// </exception>
  173. /// ----------------------------------------------------------------------------------------------------
  174.  
  175. protected virtual void EvaluateName(string name)
  176. {
  177. if (!(this.separatorB.Equals(null)) && (name.Contains(this.separatorB))) {
  178. throw new ArgumentException(message: "The parameter name cannot contain the separator character.", paramName: "name");
  179.  
  180. } else {
  181. this.nameB = name;
  182.  
  183. }
  184.  
  185. }
  186.  
  187. /// ----------------------------------------------------------------------------------------------------
  188. /// <summary>
  189. /// Evaluates an attempt to assign the parameter separator.
  190. /// </summary>
  191. /// ----------------------------------------------------------------------------------------------------
  192. /// <param name="separator">
  193. /// The parameter separator.
  194. /// </param>
  195. /// ----------------------------------------------------------------------------------------------------
  196. /// <exception cref="ArgumentException">
  197. /// The parameter separator cannot be any character contained in the parameter name.;separator
  198. /// </exception>
  199. /// ----------------------------------------------------------------------------------------------------
  200.  
  201. protected virtual void EvaluateSeparator(char separator)
  202. {
  203. if (!(string.IsNullOrEmpty(this.nameB)) && (this.nameB.Contains(separator))) {
  204. throw new ArgumentException(message: "The parameter separator cannot be any character contained in the parameter name.", paramName: "separator");
  205.  
  206. } else {
  207. this.separatorB = separator;
  208.  
  209. }
  210.  
  211. }
  212.  
  213. #endregion
  214.  
  215. }
  216.  
  217. #endregion
  218.  
  219. #region " Commandline Parameter (Of Object) "
  220.  
  221. /// ----------------------------------------------------------------------------------------------------
  222. /// <summary>
  223. /// Represents a Commandline Parameter.
  224. /// </summary>
  225. /// ----------------------------------------------------------------------------------------------------
  226. public sealed class CommandlineParameter : CommandlineParameter<object>
  227. {}
  228.  
  229. #endregion
  230.  
  231. }
  232.  
  233. //=======================================================
  234. //Service provided by Telerik (www.telerik.com)
  235. //=======================================================



Una vez tenemos la base de como representar de forma abstracta un parámetro commandline, creamos las instancias de nuestro Type para crear los parámetros.

( En este ejemplo creo un parámetro que toma como valor una cadena de texto, otro parámetro que toma como valor un booleano, y un array con dichos parámetros. )

Código
  1. private readonly CommandlineParameter<string> param1 = new CommandlineParameter<string> {
  2. Name = "/Switch1",
  3. Separator = '=',
  4. DefaultValue = "Hello World",
  5. IsRequired = true
  6. };
  7.  
  8. private readonly CommandlineParameter<bool> param2 = new CommandlineParameter<bool> {
  9. Name = "/Switch2",
  10. Separator = '=',
  11. DefaultValue = false,
  12. IsRequired = false
  13. };
  14.  
  15. CommandlineParameter[] @params = {
  16. param1,
  17. param2
  18. };

El modo en que se usuaría la aplicación sería el siguiente:

Código:
Aplicación.exe /switch1="valor1" /switch2="true" o "false"
(el orden de los parámetros no importa, y las comillas dobles tampoco, excepto para encerrar strings con espacios en blanco)



Ahora nos faltaría desarrollar el algoritmo de un método que tome como argumento (pasando por referencia) la representación de los parámetros para asignarles los valores y (pasando por valor) los argumentos commandline de la aplicación.

Nota: Adicionalmente le añadí dos parámetros que toman delegados para notificar cuando se encuentra un error de sintaxis o un parámetro requerido que no ha sido encontrado en los argumentos.

Código
  1. /// ----------------------------------------------------------------------------------------------------
  2. /// <summary>
  3. /// Loop through all the command-line arguments of this application.
  4. /// </summary>
  5. /// ----------------------------------------------------------------------------------------------------
  6. /// <param name="params">
  7. /// The commandline parameters.
  8. /// </param>
  9. ///
  10. /// <param name="callbackSyntaxError">
  11. /// An encapsulated method that is invoked if a syntax error is found in one of the arguments.
  12. /// </param>
  13. ///
  14. /// <param name="callbackMissingRequired">
  15. /// An encapsulated method that is invoked if a required parameter is missing in the arguments.
  16. /// </param>
  17. /// ----------------------------------------------------------------------------------------------------
  18. private void ParseArguments(ref CommandlineParameter[] @params, Action<CommandlineParameter> callbackSyntaxError, Action<CommandlineParameter> callbackMissingRequired) {
  19. ParseArguments(ref @params, Environment.GetCommandLineArgs.Skip(1), callbackSyntaxError, callbackMissingRequired);
  20. }
  21.  
  22. /// ----------------------------------------------------------------------------------------------------
  23. /// <summary>
  24. /// Loop through all the command-line arguments of this application.
  25. /// </summary>
  26. /// ----------------------------------------------------------------------------------------------------
  27. /// <param name="params">
  28. /// The commandline parameters.
  29. /// </param>
  30. ///
  31. /// <param name="args">
  32. /// The collection of commandline arguments to examine.
  33. /// </param>
  34. ///
  35. /// <param name="callbackSyntaxError">
  36. /// An encapsulated method that is invoked if a syntax error is found in one of the arguments.
  37. /// </param>
  38. ///
  39. /// <param name="callbackMissingRequired">
  40. /// An encapsulated method that is invoked if a required parameter is missing in the arguments.
  41. /// </param>
  42. /// ----------------------------------------------------------------------------------------------------
  43. private void ParseArguments(ref CommandlineParameter[] @params, IEnumerable<string> args, Action<CommandlineParameter> callbackSyntaxError, Action<CommandlineParameter> callbackMissingRequired) {
  44.  
  45. List<CommandlineParameter> paramRequired = (from param in @paramswhere param.IsRequired).ToList;
  46.  
  47. foreach (string arg in args) {
  48.  
  49. foreach (CommandlineParameter param in @params) {
  50.  
  51. if (arg.StartsWith(param.Name, StringComparison.OrdinalIgnoreCase)) {
  52. if (!arg.Contains(param.Separator)) {
  53. callbackSyntaxError.Invoke(param);
  54. return;
  55.  
  56. } else {
  57. string value = arg.Substring(arg.IndexOf(param.Separator) + 1);
  58.  
  59. if ((paramRequired.Contains(param))) {
  60. paramRequired.Remove(param);
  61. }
  62.  
  63. if (string.IsNullOrEmpty(value)) {
  64. param.Value = param.DefaultValue;
  65. continue;
  66.  
  67. } else {
  68. try {
  69. param.Value = Convert.ChangeType(value, param.DefaultValue.GetType());
  70. continue;
  71.  
  72. } catch (Exception ex) {
  73. callbackSyntaxError.Invoke(param);
  74. return;
  75.  
  76. }
  77.  
  78. }
  79.  
  80. }
  81.  
  82. }
  83.  
  84. }
  85.  
  86. }
  87.  
  88. if ((paramRequired.Any)) {
  89. callbackMissingRequired.Invoke(paramRequired.First);
  90. }
  91.  
  92. }
  93.  
  94. //=======================================================
  95. //Service provided by Telerik (www.telerik.com)
  96. //=======================================================



Por último, así es como usariamos dicho método:

Código
  1. static void Main(string[] args) {
  2.    ParseArguments(ref @params, args, Parameter_OnSyntaxError, Parameter_OnMissingRequired)
  3.    // Llegados a este punto todo fue exitoso. Continuar con la lógica...
  4. }
  5.  
  6. private void Parameter_OnSyntaxError(CommandlineParameter param) {
  7. Console.WriteLine(string.Format("[X] Syntax error in parameter: {0}", param.Name));
  8. Environment.Exit(exitCode: 1);
  9. }
  10.  
  11. private void Parameter_OnMissingRequired(CommandlineParameter param) {
  12. Console.WriteLine(string.Format("[X] Parameter required: {0}", param.Name));
  13. Environment.Exit(exitCode: 1);
  14. }

Nota: La declaración del array params está en el código de arriba.

Una imagen:




No se si este tocho que he escrito será demasiado tedioso (demasiado código para vagos xD) pero ahí lo dejo para quien le sirva la idea.

Saludos!
« Última modificación: 9 Diciembre 2015, 10:25 am por Eleкtro » En línea



HCK.

Desconectado Desconectado

Mensajes: 181



Ver Perfil
Re: Pasar comando desde CMD a aplicación de consola C#
« Respuesta #5 en: 11 Diciembre 2015, 15:02 pm »

Hola Elektro, estos dias no he podido estar mucho por aquí, pero menuda currada, me has hecho todo el trabajo prácticamente. De todas maneras busqué la información para aprender más, y de verdad que en general te lo agradezco mucho.  ;D

A pesar de haber empezado hace un tiempo con este lenguaje y la plataforma .NET en general, siempre voy aprendiendo a tramos por la falta de tiempo, pero me interesa mucho y por eso lo saco adelante, y con personas como tu las cosas son mas fáciles.  :)

Ya que estamos, y para no molestarte mucho mas con el asunto... ¿Algún libro que me pudieses recomendar sobre C#, a ser posible en Español (he leido que los que hay son penosos...) o en inglés pero que valga la pena pagar por él?

Gracias, si necesitas algo aquí tienes un compañero.

Un saludo!
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines