PowerShell wrapper to direct piped input to Python

2019-03-02 05:17发布

问题:

I'm trying to write a little tool that will let me pipe command output to the clipboard. I've read through multiple answers on Stack Overflow, but they didn't work for me, because they didn't include piping, or because they didn't use a function, or they just threw errors (or maybe I just messed up). I threw up my hands with PowerShell and decided to go with Python.

I created a Python script called copyToClipboard.py:

import sys
from Tkinter import Tk

if sys.stdin.isatty() and len(sys.argv) == 1:
  #We're checking for input on stdin and first argument
  sys.exit()

tk = Tk()
tk.withdraw()
tk.clipboard_clear()

if not sys.stdin.isatty():
    #We have data in stdin
    while 1:
        try:
            line = sys.stdin.readline()
        except KeyboardInterrupt:
            break

        if not line:
            break

        tk.clipboard_append(line)
elif len(sys.argv) > 1:
    for line in sys.argv[1]:
      tk.clipboard_append(line)


tk.destroy()

(I haven't fully tested the argv[1] part, so that might be shaky. I'm mainly interested in reading from stdin, so the important part is sys.stdin.)

This works great! When I'm in the directory that contains the script, I can execute something like:

ls | python copyToClipboard.py

And the contents of ls magically appear on my clipboard. That's exactly what I want.

The challenge is wrapping this in a PowerShell function that will take a piped input and simply pass the input to the Python script. My goal is to be able to do ls | Out-Clipboard, so I created something like:

function Out-ClipBoard() {
    Param(
      [Parameter(ValueFromPipeline=$true)]
      [string] $text
    )
    pushd
    cd \My\Profile\PythonScripts
    $text | python copyToClipboard.py
    popd
}

But that doesn't work. Only one line of $text makes its way to the Python script.

How can I structure the wrapper for my PowerShell script such that whatever it receives as stdin simply gets passed to the Python script as stdin?

回答1:

First, in PowerShell, a multi-line text is an array, so you need a [String[]] parameter. To solve your problem, try using the process block:

function Out-ClipBoard() {
    Param(
        [Parameter(ValueFromPipeline=$true)]
        [String[]] $Text
    )
    Begin
    {
        #Runs once to initialize function
        pushd
        cd \My\Profile\PythonScripts
        $output = @()
    }
    Process
    {
        #Saves input from pipeline.
        #Runs multiple times if pipelined or 1 time if sent with parameter
        $output += $Text
    }
    End
    {
        #Turns array into single string and pipes. Only runs once
        $output -join "`r`n" | python copyToClipboard.py
        popd
    }
}

I don't have Python here myself, so I can't test it. When you need to pass multiple items (an array) through the pipeline, you need the process block for PowerShell to handle it. More about process block and advanced functions is at TechNet.