onefish
onefish

Reputation: 85

Execute Windows Powershell command copy text to clipboard (incl. `r`n ')

I am trying to execute a powershell command to copy text to the windows clipboard including carriage returns and ALL special characters. I can execute the command ok using:

powershell.exe -command Set-Clipboard 'TEXT'

This is not in the powershell console directly so syntax differs.

I was using double quotes around text, substituting carriage returns in original text with `r`n and escaping all other special characters with ` It worked up until I got to a single ' which I understand is used by powershell to mean a literal text string.

So I changed approaches and wrapped un-escaped text in single quotes (except substituting 1 ' for 2 ''). Of course `r`n within the single quoted text are interpreted literally so doesn't work. I have tried stringing them together outside single quoted text like:

'some text here' "`r`n" 'more text here'

This works in the console but not in the command. Tried adding + either side but still does not work.

User "TessellatingHeckler" suggested -EncodedCommand but unfortunately I am unable to produce any version of base 64 encoded strings (to include in the command) which match the same string encoded via the PS console. So that is not going to work.

I have been attempting to simply substitute carriage returns in the original text with an obscure string, wrap the text in single quotes (literal) and then substitute it back to `r`n within PS. I have gotten the substitution to work in the console directly but cannot figure out how to actually send it as a command.

powershell.exe -command Set-Clipboard $Str = 'this is a test--INSERT_CRLF_HERE--1234'; $Car = '--INSERT_CRLF_HERE--'; $clr = "`r`n"; $Str = $Str -replace $Car, $clr

Can the above command be modified to work? Is it possible to achieve the intended outcome without writing to a temp file? It is preferable to be able to use single quoted text blocks as it is more robust and lightweight than trying to escape everything (even spaces) in the original text.

Upvotes: 2

Views: 2280

Answers (2)

mklement0
mklement0

Reputation: 438038

As an aside: A fully robust solution that works in any invocation scenario would indeed require use of -EncodedCommand with a string that is the Base64 encoding of the command string's UTF16-LE byte representation - but you've stated that creating such a string is not an option for you.
If you were to call from PowerShell, you could more simply use a script block (see bottom).

Update: The OP's own answer now contains a robust, if nontrivial, solution based on careful string substitutions.
The answer below may still be of interest for simpler scenarios and for background information on quoting requirements and pitfalls.


Using the scenario from your question, this simpler solution should do (verified from cmd.exe - we still don't know where you're calling PowerShell from, but I expect it to work if there's no shell involved):

powershell.exe -command "Set-Clipboard \"this is`r`na test\""

As for other special characters:

  • ' can be embedded as-is - no escaping needed
  • " requires escaping as `\"
  • $ as `$, but only if you want it to be treated as a literal (the same escaping that would apply in a regular double-quoted PowerShell string)

If your use case requires passing an arbitrary preexisting string, you'd have to employ string substitution to perform the above escaping - including replacing embedded newlines with literal `r`n.

If there is no shell involved (such as with subprocess.check_output() from Python), the above rules should suffice and make for a robust solution (assuming you only use printable characters and there are no character-encoding issues).

From cmd.exe, however, a fully robust solution that doesn't use -EncodedCommand requires extra, nontrivial work, due to its lack of proper parsing of embedded double quotes:

The following cmd.exe metacharacters typically require ^-escaping, but sometimes the mustn't be escaped:
& | < >

In the following example, & requires ^-escaping:

powershell.exe -command "Set-Clipboard \"this is`r`na ^& test\""

However, if your string also has embedded (and escaped) " chars., whether these characters require^-escaping depends on their placement relative to the embedded "; note how in the following example & need not be ^-escaped and indeed must not be, because the ^ would then become part of the string:

powershell.exe -command "Set-Clipboard \"this is`r`na 3`\" & test\""

Anticipating these variations algorithmically is a nontrivial undertaking.

Additionally, if your string had %...% tokens that look like cmd.exe-style environment variables - e.g., %FOO% - but you want to treat them as literals, the % cannot be escaped.

There is a workaround that may work, but it again depends on the presence and placement of embedded " chars.

In the following example, the "^ disrupter" trick can be used to prevent expansion of %OS%:

powershell.exe -command "Set-Clipboard \"do not expand: %^OS%\""

However, if an embedded " precedes it, the workaround becomes ineffective (the ^ is retained), and there's no direct fix that I know of in this scenario:

powershell.exe -command "Set-Clipboard \"do not expand: 3`\" %^OS%\""

You'd have to split the string into multiple pieces to break the %OS% token apart and concatenate the pieces via a PowerShell expression:

powershell.exe -command "Set-Clipboard (\"do not expand: 3`\" %\" + \"OS%\")"

Algorithmically, you could use placeholder chars. that you then replace as part of the PowerShell command, a technique you've used in your own answer.


As an aside:

Extra escaping would be required if you wanted to execute this from PowerShell:

powershell.exe -command "Set-Clipboard \`"this is`r`na test\`""

However, from PowerShell it's not hard to construct a Base64-encoded string to pass to -EncodedCommand, but there's an even easier method: use a script block to pass your command; no extra quoting requirements apply in that case, because -EncodedCommand is then automatically used behind the scenes (along with -OutputFormat Xml).
Do note that this only works from within PowerShell.

powershell.exe -command { Set-Clipboard "this is`r`na test" }

Upvotes: 1

onefish
onefish

Reputation: 85

I was informed about a rather tidy solution by Rob Simmers on another forum, which I am currently employing.

Original Text:

Test text
!@#$%^&*()_+-=[]\{}|;':",./<>?

String with 5x characters substituted ({ = {{, } = }}, ' = '', crlf = {0}, " = {1}):

Test text{0}!@#$%^&*()_+-=[]\{{}}|;'':{1},./<>?

Powershell.exe command:

powershell.exe -command "$str = 'Test text{0}!@#$%^&*()_+-=[]\{{}}|;'':{1},./<>?' -f [System.Environment]::NewLine, [Char] 0x22 ; Set-Clipboard $str"

Output (literal text placed on the clipboard) - same as the input, as desired:

Test text
!@#$%^&*()_+-=[]\{}|;':",./<>?

Upvotes: 1

Related Questions