2010-05-05 18 views
5

¿Cómo sabe un cmdlet cuando realmente debe llamar al WriteVerbose(), WriteDebug() y etc.?¿Cómo sabe un cmdlet cuándo debería llamar realmente a WriteVerbose()?

Quizás extraño algo simple pero no puedo encontrar la respuesta. Todas las implementaciones de cmdlet que he visto hasta ahora solo llaman al WriteVerbose() sin ninguna duda . Sé que es correcto para hacerlo, pero no es efectivo.

La ejecución se ve afectada cuando el modo detallado está desactivado, pero un cmdlet aún prepara los datos para la llamada WriteVerbose(), es decir, para nada.

En otras palabras, en un cmdlet Me gustaría ser capaz de:

if (<VerboseMode>) 
{ 
    .... data preparation, sometimes expensive ... 
    WriteVerbose(...); 
} 

Pero no sé cómo conseguir este if (<VerboseMode>). ¿Algunas ideas?


Conclusión: respuesta de @ El Stej muestra cómo obtener la información requerida en la teoría. En la práctica, esto es raro e improbable. Por lo tanto, si un cmdlet produce una salida verbosa o de depuración realmente costosa, parece razonable introducir un parámetro adicional que especifique los niveles de detalle.

+0

No estoy de acuerdo con su conclusión. Ver mi respuesta para una solución. –

Respuesta

5

Este es el método de System.Management.Automation.MshCommandRuntime.

internal void WriteVerbose(VerboseRecord record) 
{ 
    if ((this.Host == null) || (this.Host.UI == null)) 
    { 
     tracer.TraceError("No host in CommandBase.WriteVerbose()", new object[0]); 
     throw tracer.NewInvalidOperationException(); 
    } 
    ActionPreference verbosePreference = this.VerbosePreference; 
    if (this.WriteHelper_ShouldWrite(verbosePreference, this.lastVerboseContinueStatus)) 
    { 
     if (record.InvocationInfo == null) 
     { 
      record.SetInvocationInfo(this.MyInvocation); 
     } 
     this.CBhost.InternalUI.WriteVerboseRecord(record); 
    } 
    this.lastVerboseContinueStatus = this.WriteHelper(null, null, verbosePreference, this.lastVerboseContinueStatus, "VerbosePreference"); 
} 

MshCommandRuntime implementa la interfaz ICommandRuntime que no sabe nada acerca de la verbosidad: | (encontrado a través del reflector). La instancia de MshCommandRuntime debe estar disponible en Cmdlet (public ICommandRuntime CommandRuntime { get; set; }).

Por lo que debe ser posible emitir la propiedad CommandRuntime a MshCommandRuntime y verificar la verbosidad. De todos modos, esto es realmente feo.


Estoy totalmente de acuerdo en que debería haber una manera fácil de encontrarlo. Y además de eso compilador (soñando) debe ser lo suficientemente inteligente como para no evaluar algunas cadenas en casos como este:

$DebugPreference = 'SilentlyContinue' 
$array = 1..1000 
Write-Debug "my array is $array" 

entrada a Write-Debug será nunca utilizado, por lo $array no debería ser evaluada en cadena que se pasa .. (se es posible probar que realmente se evalúa del siguiente modo: Write-Debug "my array is $($array|%{write-host $_; $_})"

+0

@stej: Gracias por su útil investigación. Teóricamente, debería ser posible hackear de esta manera mediante la reflexión (porque la mayoría de las cosas son internas y no accesibles normalmente). Pero, por supuesto, esta no es una solución práctica. Sin embargo, aceptaré la respuesta en un momento si no encontramos mejores alternativas. También encontré una sugerencia relacionada en Connect: https://connect.microsoft.com/PowerShell/feedback/details/74811/performance-provide-formatting-overloads-for-writeverbose-writedebug-etc (Esto no lo hará ser suficiente, creo, necesitamos una bandera IsVerbose, IsDebug, etc.) –

+0

@Roman, votado. | Lo que ahora me vino a la mente: al desarrollar el cmdlet, debes tener acceso a todas las variables, ¿no es así? Entonces es posible obtener '$ DebugPrecedence' y actuar en consecuencia. No es ideal, pero debería funcionar. – stej

+1

'$ DebugPreference',' $ VerbosePreference' son mejores que nada, de hecho. Pero no son suficientes, ya que están anulados por los parámetros ubicuos de cmdlet '-Verbose',' -Debug', si corresponde. Pero estos parámetros no son accesibles desde un cmdlet. ¿Los parámetros cambian el '$ DebugPreference' local,' $ VerbosePreference' implícitamente? Lo dudo, pero intentaré averiguarlo. –

4

¿Qué tal:

BEGIN { 
    if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) { 
     $HasDebugFlag = $true 
    } 

    if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { 
     $HasVerboseFlag = $true 
    } 
} 
PROCESS { 
    if ($HasVerboseFlag) { 
     expensive_processing 
    } 
} 

Advertencia: Probado sólo en PowerShell 3.

+3

Esto es bueno, de hecho. +1. Esto debería funcionar en algunos casos, de hecho, muchos. No funcionará si los modos están configurados no por parámetros sino a través de '$ VerbosePreference' o' $ DebugPreference'. Por lo tanto, el 'if'-s también debe verificar estas variables. Pero V3 introdujo valores predeterminados personalizados. Los parámetros vinculados pueden no ser útiles si los modos están habilitados a través de los valores predeterminados personalizados. –

+1

Y no funciona si se llama a un cmdlet desde otro. –

4

Así que no solo debemos tener en cuenta los parámetros comunes -Debug y -Verbose del cmdlet en cuenta, sino también los indicadores globales $ DebugPreference y $ VerbosePreference y el hecho de que estos parámetros comunes se heredan si se llama a un cmdlet desde otro cmdlet.

Se puede hacer sin hackear el interior.

This answer muestra que se puede hacer con pequeños problemas en un cmdlet de PowerShell.

function f { [cmdletbinding()]Param() 
    $debug = $DebugPreference -ne 'SilentlyContinue' 
    $verbose = $VerbosePreference -ne 'SilentlyContinue' 
    "f is called" 
    " `$debug = $debug" 
    " `$verbose = $verbose" 
} 
function g { [cmdletbinding()]Param() 
    "g is called" 
    f 
} 
f 
f -Debug -Verbose 
g 
g -Debug -Verbose 

Desde C# tenemos que verificar estos dos indicadores globales, pero también los parámetros comunes. Asegúrese de heredar de PSCmdlet en lugar de Cmdlet para acceder al método GetVariableValue.

bool debug = false; 
bool containsDebug = MyInvocation.BoundParameters.ContainsKey("Debug"); 
if (containsDebug) 
    debug = ((SwitchParameter)MyInvocation.BoundParameters["Debug"]).ToBool(); 
else 
    debug = (ActionPreference)GetVariableValue("DebugPreference") != ActionPreference.SilentlyContinue; 

bool verbose = false; 
bool containsVerbose = MyInvocation.BoundParameters.ContainsKey("Verbose"); 
if (containsVerbose) 
    verbose = ((SwitchParameter)MyInvocation.BoundParameters["Verbose"]).ToBool(); 
else 
    verbose = (ActionPreference)GetVariableValue("VerbosePreference") != ActionPreference.SilentlyContinue; 
Cuestiones relacionadas