Windows Vista Forums

Passing arguments from .BAT to PowerShell script
  1. #1


    wofat68 Guest

    Passing arguments from .BAT to PowerShell script

    Hi,

    I'd like to have a PS script, where I can drag some files in explorer
    and drop them onto the script (for example, the dropped files are
    moved to a specific directory or something similar). This doesn't work
    with PS scripts directly. Therefore I wrote a .BAT file which is
    supposed to call the PS script. However, this doesn't work if there
    are spaces within the file names.

    Example:

    File names are:



    file with spaces 1.txt
    file with spaces 2.txt

    script.ps1:

    foreach($arg in $args) { write-host "[$arg]" }

    ..BAT file:

    powershell .\script.ps1 %*

    If I drag and drop the files onto the batch file I get the following
    output:

    F:\PowerShell>powershell .\test.ps1 "F:\PowerShell\file with spaces
    1.txt" "F:\PowerShell\file with spaces 2.txt"

    [F:\PowerShell\file]
    [with]
    [spaces]
    [1.txt]
    [F:\PowerShell\file]
    [with]
    [spaces]
    [2.txt]


    That is clearly not what I want. And in my opinion it's a bug in
    PowerShell.

    If I replace %* with "%*" I get the same results.

    If I replace %* with '%*' I get the following results:

    F:\PowerShell>powershell .\test.ps1 '"F:\PowerShell\file with spaces
    1.txt" "F:\PowerShell\file with spaces 2.txt"'

    [F:\PowerShell\file with spaces 1.txt F:\PowerShell\file with spaces
    2.txt]

    I'm running out of ideas here. Does anybody have any suggestion how to
    solve this problem?

    Thanks a lot for any help.




      My System SpecsSystem Spec

  2. #2


    Shay Levy [MVP] Guest

    Re: Passing arguments from .BAT to PowerShell script

    Hi wofat68,

    You can't drag-drop files onto a PowerShell script. For security reasons,
    PS script files are not associated with PowerShell.exe (when double-clicked
    they open in notepad).

    You can put the files in a temp directory (e.g c:\temp) and pass the directory
    path to a script as an argument:

    powershell.exe c:\foo.ps1


    ## c:\foo.ps1 ##

    dir c:\temp\*.ps1 | foreach { ...do what ever you need here...}

    ############

    ---
    Shay Levy
    Windows PowerShell MVP
    http://blogs.microsoft.co.il/blogs/ScriptFanatic



    w> Hi,
    w>
    w> I'd like to have a PS script, where I can drag some files in explorer
    w> and drop them onto the script (for example, the dropped files are
    w> moved to a specific directory or something similar). This doesn't
    w> work with PS scripts directly. Therefore I wrote a .BAT file which is
    w> supposed to call the PS script. However, this doesn't work if there
    w> are spaces within the file names.
    w>
    w> Example:
    w>
    w> File names are:
    w>
    w> file with spaces 1.txt
    w> file with spaces 2.txt
    w> script.ps1:
    w>
    w> foreach($arg in $args) { write-host "[$arg]" }
    w>
    w> .BAT file:
    w>
    w> powershell .\script.ps1 %*
    w>
    w> If I drag and drop the files onto the batch file I get the following
    w> output:
    w>
    w> F:\PowerShell>powershell .\test.ps1 "F:\PowerShell\file with spaces
    w> 1.txt" "F:\PowerShell\file with spaces 2.txt"
    w>
    w> [F:\PowerShell\file]
    w> [with]
    w> [spaces]
    w> [1.txt]
    w> [F:\PowerShell\file]
    w> [with]
    w> [spaces]
    w> [2.txt]
    w> That is clearly not what I want. And in my opinion it's a bug in
    w> PowerShell.
    w>
    w> If I replace %* with "%*" I get the same results.
    w>
    w> If I replace %* with '%*' I get the following results:
    w>
    w> F:\PowerShell>powershell .\test.ps1 '"F:\PowerShell\file with spaces
    w> 1.txt" "F:\PowerShell\file with spaces 2.txt"'
    w>
    w> [F:\PowerShell\file with spaces 1.txt F:\PowerShell\file with spaces
    w> 2.txt]
    w>
    w> I'm running out of ideas here. Does anybody have any suggestion how
    w> to solve this problem?
    w>
    w> Thanks a lot for any help.
    w>



      My System SpecsSystem Spec

  3. #3


    Alex K. Angelopoulos Guest

    Re: Passing arguments from .BAT to PowerShell script

    Wofat,

    Here's one workaround with PowerShell 1.0 that works. The trick is to
    generate a complete command line and feed it as input to PowerShell, using
    the - argument, using a batch file like this:

    @echo off
    :: pswrapper.cmd
    echo.\script.ps1 %* | powershell -NoExit -Command -



    "wofat68" <atlanticcoast2006@xxxxxx> wrote in message
    news:5dfe34e9-5747-4c05-ae44-9fe4d958ae3f@xxxxxx

    > Hi,
    >
    > I'd like to have a PS script, where I can drag some files in explorer
    > and drop them onto the script (for example, the dropped files are
    > moved to a specific directory or something similar). This doesn't work
    > with PS scripts directly. Therefore I wrote a .BAT file which is
    > supposed to call the PS script. However, this doesn't work if there
    > are spaces within the file names.
    >
    > Example:
    >
    > File names are:
    >
    > file with spaces 1.txt
    > file with spaces 2.txt
    >
    > script.ps1:
    >
    > foreach($arg in $args) { write-host "[$arg]" }
    >
    > .BAT file:
    >
    > powershell .\script.ps1 %*
    >
    > If I drag and drop the files onto the batch file I get the following
    > output:
    >
    > F:\PowerShell>powershell .\test.ps1 "F:\PowerShell\file with spaces
    > 1.txt" "F:\PowerShell\file with spaces 2.txt"
    >
    > [F:\PowerShell\file]
    > [with]
    > [spaces]
    > [1.txt]
    > [F:\PowerShell\file]
    > [with]
    > [spaces]
    > [2.txt]
    >
    >
    > That is clearly not what I want. And in my opinion it's a bug in
    > PowerShell.
    >
    > If I replace %* with "%*" I get the same results.
    >
    > If I replace %* with '%*' I get the following results:
    >
    > F:\PowerShell>powershell .\test.ps1 '"F:\PowerShell\file with spaces
    > 1.txt" "F:\PowerShell\file with spaces 2.txt"'
    >
    > [F:\PowerShell\file with spaces 1.txt F:\PowerShell\file with spaces
    > 2.txt]
    >
    > I'm running out of ideas here. Does anybody have any suggestion how to
    > solve this problem?
    >
    > Thanks a lot for any help.
    >
    >
    >

      My System SpecsSystem Spec

  4. #4


    wofat68 Guest

    Re: Passing arguments from .BAT to PowerShell script

    Thanks for the response.

    I guess I provided a misleading example. Just for clarification:

    1. PS's security restrictions are fine with me. That's the reason I
    used the intermediate batch file.

    2. I still claim that the way PS parses the command line is wrong
    here. As you can see in the examples, CMD.EXE encapsulates the
    arguments correctly within double quotes. PS seems to ignore the
    quotes.

    3. I was looking for a general purpose solution, not just moving
    files. My example was bad. In general I want to select some files in
    explorer, move (drag) the selected files and drop them onto a script/
    batch. Then the script should do with the files whatever I want.
    Examples: renaming the files, changing time stamps, calculating check
    sums, and so on.

    Anyway, your suggestion lead me to another idea: writing the arguments
    into a temporary file and then reading the file line by line. Quotes
    have to be removed explicitely.

    ..BAT file:

    del /q /f %TEMP%\ps.args 2>NUL
    for %%f in (%*) do echo %%f>>%TEMP%\ps.args
    powershell .\script.ps1 %TEMP%\ps.args

    script.ps1:

    foreach($arg in get-content $args[0]) { write-host "[$
    ($arg.SubString(1, $arg.Length - 2))]" }


    Thanks for the help.

    --wofat68


      My System SpecsSystem Spec

  5. #5


    wofat68 Guest

    Re: Passing arguments from .BAT to PowerShell script

    This is pretty nice. Much better than the solution I finally came up
    with.

    Thanks a lot.

    --wofat68

      My System SpecsSystem Spec

  6. #6


    Alex K. Angelopoulos Guest

    Re: Passing arguments from .BAT to PowerShell script

    Comments on a couple of your points here...

    "wofat68" <atlanticcoast2006@xxxxxx> wrote in message
    news:628a91ec-b8d7-443d-8601-c60c96fdaad1@xxxxxx

    > Thanks for the response.

    > 2. I still claim that the way PS parses the command line is wrong
    > here. As you can see in the examples, CMD.EXE encapsulates the
    > arguments correctly within double quotes. PS seems to ignore the
    > quotes.
    Although I agree the commandline parsing is poor, this is not PowerShell. It
    appears that at a lower level the standard argument parsing done by Windows
    APIs causes the double-quote stripping. The same thing happens with WSH;
    there is simply no way to pass a literal " to a script within an argument.

    I'm not sure about the precise details of how things are implemented in
    PowerShell 2.0, but they do have a -File parameter for PowerShell in V2 that
    correctly handles a script file and arguments with embedded spaces. I
    believe what it does is to simply pass everything straight through to the
    script as an argument array, which modifies how data is interpreted but
    makes it more straightforward for wrapper scripts.

    > 3. I was looking for a general purpose solution, not just moving
    > files. My example was bad. In general I want to select some files in
    > explorer, move (drag) the selected files and drop them onto a script/
    > batch. Then the script should do with the files whatever I want.
    > Examples: renaming the files, changing time stamps, calculating check
    > sums, and so on.
    Using the input stream as I suggested should do the trick. There are a
    couple of other techniques, including using an intermediary WSH script that
    reassembles arguments inserting SINGLE quotes where you need quoted
    arguments, but I think the input stream command technique is easiest for
    drag-and-drop. After all, you aren't going to be using the input stream
    anyway in that context.

    > Anyway, your suggestion lead me to another idea: writing the arguments
    > into a temporary file and then reading the file line by line. Quotes
    > have to be removed explicitely.
    And that's yet another way




      My System SpecsSystem Spec

  7. #7


    wofat68 Guest

    Re: Passing arguments from .BAT to PowerShell script

    Alex,

    Thanks for your detailed response. I read your first post after my
    response to Shay. So it got a little bit confusing. Sorry for that.

    Anyway. Your suggestion is better than mine. It doesn't involve any
    temporary files.

    Regarding the command line parsing: this would probably require more
    research. But I won't go into that. Your solution works perfectly for
    me, even if it's "only" a workaround.

    --wofat68

      My System SpecsSystem Spec

  8. #8


    wofat68 Guest

    Re: Passing arguments from .BAT to PowerShell script

    On Aug 1, 4:11*pm, "Alex K. Angelopoulos" <aka(at)mvps.org> wrote:


    > @echo off
    > :: pswrapper.cmd
    > echo.\script.ps1 %* | powershell -NoExit -Command -
    After some testing, I found that this does not work on some strings,
    unfortunately. If the argument does *not* contain any spaces, it isn't
    quoted by CMD.EXE. This results in a problem, if the file name
    contains parenthesis. Obviously, PS is trying to interpret that as an
    expression.

    This may be also a security problem, since code could be injected.

    I just wanted to let you know.

    Probably I have to use my solution using a temporary file.

    --wofat68

      My System SpecsSystem Spec

  9. #9


    Alex K. Angelopoulos Guest

    Re: Passing arguments from .BAT to PowerShell script

    Comments inline, and another solution with some "special" guard rails for
    drag-and-drop specifically.

    "wofat68" <atlanticcoast2006@xxxxxx> wrote in message
    news:8a96d697-8c8c-433c-a09e-80bc9f94fa38@xxxxxx

    > On Aug 1, 4:11 pm, "Alex K. Angelopoulos" <aka(at)mvps.org> wrote:
    >
    >

    >> @echo off
    >> :: pswrapper.cmd
    >> echo.\script.ps1 %* | powershell -NoExit -Command -
    >
    > After some testing, I found that this does not work on some strings,
    > unfortunately. If the argument does *not* contain any spaces, it isn't
    > quoted by CMD.EXE. This results in a problem, if the file name
    > contains parenthesis. Obviously, PS is trying to interpret that as an
    > expression.
    Yes, that wasn't universal. In any case, you can still encounter problems
    even with doublequotes, since several characters that are allowed in file
    and folder names can cause serious problems to PowerShell - (){}[]$`' are
    all legal.

    > This may be also a security problem, since code could be injected.
    >
    > I just wanted to let you know.
    >
    The other solution I have goes back to the through-argument passing, but I'm
    not sure I see this. If anything was being piped in, it could theoretically
    expose something, but since the batch wrapper itself invokes the
    interactively-specified data explicitly as arguments, you should get no more
    and no less vulnerability than you have to injection via the script
    arguments.

    This did suggest another point to me, however. Since this is explicitly
    designed to be a drag-and-drop wrapper, items should _always_ be resolvable
    as filesystem paths. Therefore, it makes sense for a wrapper to actually
    _test_ for this condition first, and discard any input that isn't a valid
    path. I've added that to a new solution.

    > Probably I have to use my solution using a temporary file.
    Possibly, but from what I can tell I think there will still be some issues.
    The ideal technique is to pass the items in as single-quoted rather than
    double-quoted strings. I've tried out another wrapper script, which is shown
    below. This is actually a WSH script, and theoretically could be made more
    generic, but for now it acts as a wrapper akin to the batch file concept.
    Details:

    (1) The script is totally generic, as long as you observe name and location
    constraints. You may want to modify the -NoExit and possibly force the
    PowerShell subscript to run hidden, but those are one-off modifications to
    this script.

    (2) As written, the WSH wrapper needs to be in the same location as the PS
    script, with the same base name - e.g., if the PowerShell script is at
    c:\apps\scripts\newdemo.ps1, then the WSH script needs to be
    c:\apps\scripts\newdemo.vbs. You can of course put a shortcut to the WSH
    script anywhere.

    (3) The script is _explicitly_ designed for drag-and-drop. It tests every
    single argument it was passed to see if it is an existing file or folder
    path. If not, the item is dropped silently. Your comment about potential
    code injection suggested this addition to me. Since we shouldn't get
    anything _but_ file/folder paths as arguments, why not explicitly match the
    argument data?

    (4) The script works by explicitly single-quoting every argument, whether it
    has embedded spaces or not. If an argument contains an embedded single-quote
    (it's a legal filename character) then it is escaped by doubling. This
    prevents use of any special tricks with filesystem names, but since it's
    drag-and-drop, it won't matter.

    (5) The only escaping done to the script name itself is escaping spaces in
    the script name with a preceding backtick. This allows you to run PowerShell
    scripts in situ from add-on installation folders that might be under Program
    Files, or from per-user application folders.



    ' BEGIN WSH wrapper script
    ' Should have same basename as PS script it will run,
    ' and must be in same folder as PS script.
    ' ex: if c:\tmp\fred.ps1, this must be c:\tmp\fred.vbs

    dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
    Dim WshScript: WshScript = WScript.ScriptFullName
    Dim PsScript
    PsScript = fso.BuildPath( _
    fso.GetFile(WshScript).ParentFolder.Path, _
    fso.GetBaseName(WshScript) & ".ps1")

    ' PowerShell scripters should probably know better than
    ' using a script name containing embedded spaces, but this
    ' may be unavoidable for some scripts.
    ' Just in case, we escape spaces with a backtick.
    PsScript = Replace(PsScript, " ", "` ")


    Dim i, arg
    i = 0
    Dim ArgSet: Set ArgSet = CreateObject("Scripting.Dictionary")
    Argset(i) = PsScript

    For each arg in WScript.Arguments
    ' EXPLICITLY ensure these resolve to file/folder paths
    if fso.FileExists(arg) or fso.FolderExists(arg) then
    i = i + 1
    ' Include escapes for single-quotes used in file/folder names
    Argset(i) = "'" & Replace(arg, "'", "''") & "'"
    End If
    Next

    ' Remove -NoExit if you don't want PS to stay open when done.
    Dim Command
    Command = "powershell -NoLogo -NoExit -Command ""& {" _
    & Join(ArgSet.Items) _
    & "}"""

    ' WScript.Echo "command as passed to PowerShell:", Command
    Dim WshShell: Set WshShell = CreateObject("WScript.Shell")

    WshShell.Run Command
    ' Alternative operation (runs script hidden - ONLY use if
    ' you also remove -NoExit:
    ' WshShell.Run Command, 0

    ' END WSH Wrapper Script



      My System SpecsSystem Spec

Passing arguments from .BAT to PowerShell script problems?

Similar Threads
Thread Thread Starter Forum Replies Last Post
Passing Arguments capt edgar VB Script 5 05 Jan 2010
Passing Alert GUID to Powershell Script Steve Giordano PowerShell 1 04 Jan 2010
Hosting PowerShell & passing parameters to script Dmitry Naumov PowerShell 9 24 Sep 2007
passing arguments VB PowerShell 1 19 Dec 2006
Executing PS1 script from CMD/SchTasks and passing arguments Terry E Dow PowerShell 1 27 Nov 2006