{"id":332,"date":"2006-11-28T06:00:00","date_gmt":"2006-11-28T14:00:00","guid":{"rendered":"http:\/\/testblog.sapien.com\/index.php\/2006\/11\/28\/powershell-transcripts\/"},"modified":"2006-11-28T06:00:00","modified_gmt":"2006-11-28T14:00:00","slug":"powershell-transcripts","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2006\/11\/28\/powershell-transcripts\/","title":{"rendered":"PowerShell Transcripts"},"content":{"rendered":"<p>PowerShell has an interesting cmdlet that lets you save a transcript of a PowerShell session. Every command you run and the resulting output can be recorded to a text file. This file can be used as a training aid, an audit trail, or as the source for rapid PowerShell script development.<\/p>\n<p>To begin recording, use the Start-Transcript cmdlet simply by specifying a filename:<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">start-transcript c:\\transcript.txt<\/span><\/p>\n<p>From this point, every shell command and output will also be recorded in the transcript file. To stop recording run<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">stop-transcript<\/span><\/p>\n<p>If you open the file in Notepad (or use Get-Content) you&#8217;ll see&nbsp; header and footer sections plus every command you ran. You&#8217;ll see that each command includes the PowerShell prompt, which defaults to a directory path. Since a .ps1 file is really nothing more than a bunch of PowerShell expressions, you could take the transcript file, parse out the expressions and have the beginnings of a PowerShell script.<\/p>\n<p>I&#8217;ve created a function, as demonstrated in this script, that takes a PowerShell transcript and creates a text file (presumably a ps1 file) with just the commands or expressions.<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\"># ParseTranscript.ps1<\/span><br \/><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Function Parse-Transcript {<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># Take a PowerShell transcript and parse it for commands,<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># sending each line to a new script file which can then<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># be further edited.<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># transcript source example<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># $log=&#8221;c:\\transcript.txt&#8221;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># filename for script to be created from transcript<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># $script=&#8221;c:\\TempScript.ps1&#8243;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Param ([string]$log=$(Throw &#8220;You need to specify a transcript file name&#8221;),[string]$script=$(Throw &#8220;You need to specify a filename for the output&#8221;))<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">if (! (test-path $log)) {<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">throw &#8220;$($log) can&#8217;t be found!&#8221;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">}<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">#Create new file and overwrite any existing file<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">&#8220;# Script Created &#8220;+(Get-Date).ToString() | `<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Out-File -filepath $script<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">&#8220;# Source file: &#8220;+$log |Out-File -filepath $script -append<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">&#8220;# Author: &#8220;+ ((get-childitem env:username).value).ToUpper() | `<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Out-File -filepath $script -append<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">$filteredLog=Get-Content $log |Select-String &#8220;&gt; &#8220;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\"># drop the last line which is likely a stop-transcript command<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">for ($i=0;$i -lt $filteredLog.length-1;$i++) <\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">{<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">&nbsp;&nbsp;&nbsp; $tmp=($filteredLog[$i].ToString()).split(&#8220;&gt;&#8221;)<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">&nbsp;&nbsp;&nbsp; ($tmp[1]).Trim() | Out-File -filepath $script -append<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">}<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Write-Host &#8220;Finished parsing&#8221; $log&#8221;. See&#8221; $script &#8220;for your script.&#8221;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">}<\/span><br style=\"font-family: Courier New,Courier,mono;\"><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">$source=Read-Host &#8220;What is the filename and path of your transcript file?&#8221;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">$dest=Read-Host &#8220;What is the filename and path for the ps1 output file?&#8221;<\/span><br style=\"font-family: Courier New,Courier,mono;\"><span style=\"font-family: Courier New,Courier,mono;\">Parse-transcript $source $dest<\/span><\/p>\n<p>The function needs the filename and path of the transcript file and a filename for the output file.<\/p>\n<p>The function uses Select-String to only pull the lines that have &#8220;&gt; &#8220;, which should be the end of each prompt, and creates $filteredLog<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">$filteredLog=Get-Content $log |Select-String &#8220;&gt; &#8220;<\/span><\/p>\n<p>Normally you would use a ForEach expression to parse out $filteredLog. However, I&#8217;m assuming I won&#8217;t need the last expression entered which will be a Stop-Transcript command. Therefore, I&#8217;m using a For expression to get every line but the last one.<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">for ($i=0;$i -lt $filteredLog.length-1;$i++) <\/span><\/p>\n<p>The real work is splitting the line at &gt;. I create a second temporary array to hold the results of the Split method:<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">&nbsp;$tmp=($filteredLog[$i].ToString()).split(&#8220;&gt;&#8221;)<\/span><\/p>\n<p>Finally I get the second element of the temporary array which should be a PowerShell command.&nbsp; I use Trim to get rid of any leading or trailing spaces:<\/p>\n<p><span style=\"font-family: Courier New,Courier,mono;\">($tmp[1]).Trim() | Out-File -filepath $script -append<\/span><\/p>\n<p>This process is repeated for every line in $filteredlog except the last one. That&#8217;s all there is to it. When I&#8217;m finished I have a .ps1 file that could be run as a script, or most likely edited and refined in PrimalScript.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>PowerShell has an interesting cmdlet that lets you save a transcript of a PowerShell session. Every command you run and the resulting output can be recorded to a text file. This file can be used as a training aid, an audit trail, or as the source for rapid PowerShell script development.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[2,25],"tags":[],"class_list":["post-332","post","type-post","status-publish","format-standard","hentry","category-general","category-windows-powershell"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=332"}],"version-history":[{"count":0,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/332\/revisions"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}