How to write contents of an RTF file to Windows clipboard with one-liner?

Viewed 259

I know practically nothing about PowerShell but need to do a very specific task: develop a one-liner to write the contents of a RTF file to the Windows clipboard.

I have separate pieces that work, but lack the syntax knowledge to hook it all together.

This little script works, if saved to blah.ps1 then invoked as ./blah.ps1, but I need to avoid using a script.

$data = New-Object System.Windows.Forms.DataObject
$rtf = Get-Content -Path foo.rtf -Raw
$data.SetData([System.Windows.Forms.DataFormats]::Rtf, $rtf)
[System.Windows.Forms.Clipboard]::SetDataObject($data)

But I need to express it as a one-liner more like this:

Get-Content foo.rtf | $data = New-Object System.Windows.Forms.DataObject; $data.SetData([System.Windows.Forms.DataFormats]::Rtf, $_); [System.Windows.Forms.Clipboard]::SetDataObject($data)

which fails with

At line:1 char:24
+ Get-Content foo.rtf | $data = New-Object System.Windows.Forms.DataOb ...
+                        ~~~~~
Expressions are only allowed as the first element of a pipeline.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
2 Answers

Semicolons (;) are used to mimic a newline in one-line scripts, Pipe characters (|) are used to pass outputs down the pipeline as inputs, you can turn this into a one-liner that doesn't need either though just by wrapping them inside each other:

[Windows.Forms.Clipboard]::SetDataObject([Windows.Forms.DataObject]::new([Windows.Forms.DataFormats]::Rtf,(Get-Content -Raw "C:\path\to\file.rtf")))

You will likely need to "Add" forms as well, so if that doesn't work:

Add-Type -AssemblyName "System.Windows.Forms"|Out-Null;[Windows.Forms.Clipboard]::SetDataObject([Windows.Forms.DataObject]::new([Windows.Forms.DataFormats]::Rtf,(Get-Content -Raw "C:\path\to\file.rtf")))

I have now used @colsw's very helpful suggestion to come up with a better solution. One downside of my original code is that the RTF only persists on the clipboard until the PowerShell instance exits. This is not a problem during interactive development, but it was a dealkiller in my actual usage. Update: I now know you can call the SetDataObject method with $true as the second argument (the copy argument), to make the clipboard data persist.

It turns out that you can use a simpler solution to write rich text, which also has the benefit of better persistence.

(If I really wanted a script, I wouldn't hardwire the RTF file path like this, but in my real use case, I have to interpolate a file name at run time.)

Here it is in script form:

Add-Type -AssemblyName System.Windows.Forms
$rtf = Get-Content -Path foo.rtf
[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Rtf)

And as an ugly one-liner:

powershell -Command "Add-Type -AssemblyName System.Windows.Forms | Out-Null;[Windows.Forms.Clipboard]::SetText((Get-Content foo.rtf),[Windows.Forms.TextDataFormat]::Rtf)"
Related