Home PowerShell Internals ISE Editor Start-Transcript for ISE Editor

Start-Transcript for ISE Editor

Add transcript support to PowerShell ISE editor

With a free module, you can add transcript support to the ISE editor and any other PowerShell host that is not capable of transcription

With Start-Transcript and Stop-Transcript, you can easily record all interactive commands (plus output) to a log file. While these cmdlets by default work only in the PowerShell console, we created a little module that adds this capability to the PowerShell ISE editor (and any other PowerShell host that lacks transcription support).

Adding Support For Transcription

Transcription is controlled by the cmdlets Start-Transcript and Stop-Transcript but implemented by the host. So while both cmdlets are present in the ISE editor, they throw an exception at you when you try and use them. ISE simply did not implement transcription support.

Which is too bad because logging what you do can be a great thing. It can auto-document what you did during a training or experimental session, and it can auto-document entire scripts by capturing their output.

So to add transcription support, you have to code it yourself. That’s what we have done for you. All you need is this free module:

Start-Transcript for ISE editor
3.7 KiB

After downloading, unblocking and unpacking the module, you just need to import the module to add transcription support:

Import-Module Transcript

Next, ISE behaves just like a plain PowerShell console, and you can enable and disable transcription using Start-Transcript and Stop-Transcript. Refer to their help on how to use the optional parameters.

With the module "transcript", you can transcribe in the ISE editor

Transcription will by default automatically stop when you close the host. You can stop transcription anytime manually by using Stop-Transcript. Unless you specified your own file name, PowerShell generates an automatic file name for the log file and returns it when you start and stop the transcript. When you open this file, it shows all commands you entered and all results delivered by these commands:

This is what a transcript might look like

How Transcription Works

If you don’t care how all of this works as long as it does work, skip the rest of this article. However, by looking at how this module accomplishes its task, you can learn a lot of new things.

Whenever something is outputted into the console (or the ISE), PowerShell internally calls Out-Default. So in order to implement transcription support, you would have to override Out-Default and replace it with your own Out-Default function. That’s how the module does it.

However, your own Out-Default function must call the original Out-Default cmdlet at the end, or else there would be no visible output any longer.  And this is where things become a bit tricky.

Overriding Out-Default with a Proxy Function

Your own function needs to receive pipeline data (just like Out-Default does), but it also must send this pipeline data immediately – piece by piece – to Out-Default. If your function just collected all incoming data, did the logging and then handed the data over to the original Out-Default, then this would work. It would break the real-time behavior of the pipeline, though. You would have to wait for all results to be produced until output appeared.

This is why the module created a proxy function for Out-Default. This way, it can individually control how its begin-, process– and end-blocks are called. That’s important because you not only want to log the results of a command but also the command line that produced them. So inside the proxy function in its process-block, the calling command can be retrieved using $MyInvocation from the parent scope.

if ($global:isTranscribing)
  if ($OriginalCommand -eq $null)
    $OriginalCommand = '{0}{1}' -f (prompt), (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Definition

This imposes another challenge: the command results are emitted before the function can know the command line that produced them, but the log file should of course log the command line first and their results second.

Out-StringEx: Converting Pipeline Data To String Without Touching It

So the command results need to be stored somewhere. To not store huge amounts of data, the data needs to be converted to text before it is stored. This is done by another proxy function called Out-StringEx which is used internally and is defined in the psm1 file of the module.

This function takes arbitrary input over the pipeline and works almost like Out-String. It does convert incoming objects to text (so you can collect and save them using its -OutVariable parameter). But unlike the original Out-String, it continues to return the untouched original objects. You can try this if you want because Out-StringEx is publicly exposed:

PS> get-date | Out-StringEx -OutVariable test | Select-Object *

DisplayHint : DateTime
DateTime    : Montag, 29. Oktober 2012 11:41:30
Date        : 29.10.2012 00:00:00
Day         : 29
DayOfWeek   : Monday
DayOfYear   : 303
Hour        : 11
Kind        : Local
Millisecond : 820
Minute      : 41
Month       : 10
Second      : 30
Ticks       : 634871076908205519
TimeOfDay   : 11:41:30.8205519
Year        : 2012

PS> $test

Montag, 29. Oktober 2012 11:41:30


The overriden Out-Default uses Out-StringEx inside its wrapped command:

$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Out-Default', 'Cmdlet')
if ($global:isTranscribing)
  $scriptCmd = { Out-StringEx | & $wrappedCmd }

So if transcription is turned on, it prepends the original Out-Default with Out-StringEx. Before the data gets send to Out-Default for output, it passes Out-StringEx and gets collected as string in a variable. The content of this variable is then in the end-block appended to the log file:

if ($global:isTranscribing)
  $OriginalCommand | Out-File -Append -FilePath $global:TranscriptPath
  $script:TranscriptContent | Out-File -Append -FilePath $global:TranscriptPath

Lots of Room For Extensions

And this is the beauty of this concept: once you wrapped your head around this, you can easily control just about every aspect of logging. If you wanted, you could, for example, log commands only (no results). You could add a timestamp to the commands (indicating when they were issued and, if you compared them to the previous timestamp, how long each command took). Or, you could exclude errors from your log.

So please let us know if this works for you and whether you made adjustments. We sure would appreciate you giving back to the community by submitting your feedback.

For now, we hope this was fun for you! In case you want to dig deeper and maybe get special high-class PowerShell training, then if you live in Europe, we are more than happy to set one up for you. Just drop a line to [email protected]!

Start-Transcript for ISE editor
3.7 KiB