James O'Neill's Blog

September 11, 2011

Adding $Clipboard automatic variable support to PowerShell

Filed under: Powershell — jamesone111 @ 5:08 pm

Some of the bits and pieces I have been working on recently have needed PowerShell to take input from the windows clipboard, and as with so many things in PowerShell there is more than one way this can be done.

  • If it is something short paste into a command line –wrapping in quotes or @” “@ as required
  • Paste into a new edit window in the PowerShell ISE and wrap it with $variableName = “” (or @” “@ as required) , then run the contents of the Window
  • Use  [windows.clipboard]::GetText() in a command line
  • Create a Get-Clipboard Function which wraps  [windows.clipboard]::GetText()
  • Create a $Clipboard “automatic variable”
  • Use the Paste.Exe which Joel has on his site.

Note (a) that if you want to use  [windows.clipboard]::GetText() in the “Shell” version of PowerShell you need to start PowerShell.exe with the –STA switch and run the command   Add-Type -AssemblyName (“Presentationcore”) – I’m actually only interested in doing this in the ISE.
and (b) If you want to send PowerShell’s output to the clipboard the command line Clip.exe has been Windows for several versions. It’s getting text from the clipboard which needs more work.

There’s very little to choose between having Get-Clipboard and $clipboard but having just read Robert’s piece on Tied Variables I thought I would go down that path.

Robert explained that you can set a breakpoint on a PowerShell variable to run a scriptblock when the variable is read, so he got to thinking “I can run something which changes the value of variable, when it is read.

In my profile I have

$Global:MyBreakPoint = $Global:Clipboard = Set-PSBreakpoint -Variable Clipboard -Mode Read -Action {
Set-Variable clipboard ([Windows.clipboard]::GetText()) -Option ReadOnly, AllScope -Scope Global -Force}

So $clipboard is set to the breakpoint which is created by monitoring read action on itself. At each subsequent read the script-block is called and uses set-variable to make $clipboard a ReadOnly variable holding the value of the clipboard at that moment.

In the I modified my Get-SQL command to take advantage of this. I added a –Paste switch (I chose –paste over –clipboard simply because I already had –connection so –c would be ambiguous –paste allows me to run sql –p , taking advantage of SQL being an automatic alias for Get-SQL) then

if ($Paste -and (test-path 'Variable:\Clipboard'))
    {$sql = $Clipboard -replace ".*(?=select|update|delete)","" -replace "[\n|\r]+","" }

This trims out anything before “Select”, “update” or “Delete” –as I am usually copying a statement from a log with a time stamp at the start, and it also replaces any carriage return/line feed characters.

WHAT ? A bug in the PowerShell ISE

imageThis was all working brilliantly until I went to use a function which uses the PowerShellISE object model to manipulate text in an edit window it fails – the error implies that PowerShell’s object model thinks a debugger is running if a break point exists, even though trying to edit the script files interactively is not a problem .

To get around this I set TWO variables to point to the break point, one was $clipboard, which will be overwritten as soon as it is used, and the other is $myBreakPoint. In my edit function I now start with

if ($MyBreakPoint) {Remove-PSBreakpoint $MyBreakPoint }

Removing the breakpoint doesn’t remove the variable $myBreakpoint so on the way out of the function I use it to decide if I need to reinstate it.

September 2, 2011

Round-tripping files for editing

Filed under: Powershell — jamesone111 @ 9:20 pm

Watching a quiz the other day the host said of a book “I plan to start [it] the moment my Doctor tells me I have 2000 years to live”. It’s was a great spin on “life’s too short to…” I my case life is too short to re-learn how to use vi.  I learned how to use it on Wyse terminals at university in the mid 1980s, and I didn’t rate it then. If wikipedia has its facts right vi was dates back to 1976; Steve Jobs and Steve Wozniak had yet to launch the Apple II had IBM hadn’t even thought of making a PC, and the supercomputers in the best equipment research facilities had less power than today’s SmartPhones. In computer terms dinosaurs were still roaming the earth, and must make vi the coelacanth of software, ugly as hell and somehow immune to the forces which should have rendered it extinct.

I’m working with a Linux server which I can only access over an SSH session. Since configuring the server means editing text files some thought has gone into how to avoid vi.  We’ve standardized on SSH explorer in the office – it can round-trip files to the PC for editing. I’ve recently been using the netcmdlets to move files around and I’ve also been looking at trapping file-changed events I found myself asking “How difficult would it be to transfer a file to a temporary folder, load it into the editor of my choice and push it back to where it came from when it was saved?”. The answer turns out “about this difficult …”

Function Edit-RemoteItem {
param ([Parameter(ValueFromPipeLine=$true, ValueFromPipelineByPropertyName=$true, mandatory=$true)]
      
[String]$path,
       $Connection = $Global:ssh
       )
       $localpath     =  ( join-path $env:temp ($path -split "/")[-1])
       $MessageDataHT = @{   Server=$connection.Server;        RemoteFile=$path; 
                         Credential=$connection.Credential;      LocalFile=$localpath; 
                              force=$true;                       Overwrite=$true}
       Get-SFTP @MessageDataHT | out-null
       if ( $Host.Name -match "\sISE\s" )
            { $null = $psISE.CurrentPowerShellTab.Files.Add($localpath) }
       else { notepad.exe $localpath }
       $Global:watcher = Register-FileSystemWatcher -MessageData $MessageDataHT -Path $localpath `
                       -On "Changed" -Process {
                         
$event.sender.enableRaisingEvents = $false 
                          $oldpp =$ProgressPreference
                          $ProgressPreference="silentlycontinue"
                          if ((Send-SFTPserver 
$event.MessageData.server `
                                      -LocalFile  $event.MessageData.LocalFile `
                                      -RemoteFile $event.MessageData.RemoteFile `
                                      -Credential $event.MessageData.Credential` 
                                      -Overwrite -Force ).size) {
                            
Write-host "Updated $($event.MessageData.RemoteFile on $($event.MessageData.server)"
                         
}
                          $ProgressPreference = $oldpp 
                          $event.sender.enableRaisingEvents = $True
                      
}
}
To run through the function quickly; it takes a remote path and a netcmdlets SSH connection object.  It builds a local path and a hash table with parameters in and calls Get-SFTP by splatting – turning each key/value pair in the hash table into a parameter/value pair.

If it is running in the PowerShell ISE it loads the transferred file into PowerShell’s editor, otherwise it loads it into notepad.

Then it sets up a FileSystem watcher using the same code I talked about here  The watcher is passed the same hash table of parameters – unfortunately you can’t use splatting in a process block and there is no option to hide the progress bar, so I temporarily change the $Progress preference and change it back when I’ve finished. I also make sure that while the upload is happening no new events are raised. In between I call send-Sftp inserting parameters from the hash table: simples.

Blog at WordPress.com.