添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

For years, I have used the cmd/DOS/Windows shell and passed command-line arguments to batch files. For example, I have a file, zuzu.bat and in it, I access %1 , %2 , etc. Now, I want to do the same when I call a PowerShell script when I am in a Cmd.exe shell . I have a script, xuxu.ps1 (and I've added PS1 to my PATHEXT variable and associated PS1 files with PowerShell). But no matter what I do, I seem unable to get anything from the $args variable. It always has length 0.

If I am in a PowerShell shell, instead of cmd.exe , it works (of course). But I'm not yet comfortable enough to live in the PowerShell environment full time. I don't want to type powershell.exe -command xuxu.ps1 p1 p2 p3 p4 . I want to type xuxu p1 p2 p3 p4 .

Is this possible, and if so, how?

The sample I cannot get to work is trivial, foo.ps1:

Write-Host "Num Args:" $args.Length;
foreach ($arg in $args) {
    Write-Host "Arg: $arg";

The results are always like this:

C:\temp> foo
Num Args: 0
C:\temp> foo a b c d
Num Args: 0
c:\temp>
  

-File

Runs the specified script in the local scope ("dot-sourced"), so that the functions and variables that the script creates are available in the current session. Enter the script file path and any parameters. File must be the last parameter in the command, because all characters typed after the File parameter name are interpreted as the script file path followed by the script parameters.

powershell.exe -File "C:\myfile.ps1" arg1 arg2 arg3

means run the file myfile.ps1 and arg1 arg2 & arg3 are the parameters for the PowerShell script.

This still doesn't help with what the op wants ("I want to type xuxu p1 p2 p3 p4"). – thdoan Jun 22 '18 at 10:04

OK, so first this is breaking a basic security feature in PowerShell. With that understanding, here is how you can do it:

  • Open an Windows Explorer window
  • Menu Tools -> Folder Options -> tab File Types
  • Find the PS1 file type and click the advanced button
  • Click the New button
  • For Action put: Open
  • For the Application put: "C:\WINNT\system32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" %*
  • You may want to put a -NoProfile argument in there too depending on what your profile does.

    I think the key is in your step 6 where you pass in the parameters to powershell.exe. Daniel says he has associated PS1 files with PowerShell but that doesn't pass arguments without the extra %1 %* specs. Also note that the -File parameter is not available in V1. It is new to V2. – Keith Hill Aug 18 '09 at 16:25 I'll have to install V2 before I can try your suggestion. Thanks. When you say this is breaking a basic security feature, what does "this" you mean? Calling a PowerShell script from Cmd.exe as though it were a .com/.bat/.exe file? Passing parameters to the script? – Daniel 'Dang' Griffith Aug 19 '09 at 3:32 Sorry I should have been more clear. The calling the script without explicitly calling powershell.exe. I'm not saying it is a significant security feature for you personally and it is security through obscurity which I am not always a fan of anyway. – EBGreen Aug 19 '09 at 12:19 To add to EBGreen's comment, the basic security problem that PowerShell's tries to avoid is folks double-clicking on PS1 files attached to email and having the script run. That's why PS1 files are only associated with an editor by default. Microsoft really doesn't want a PowerShell version of the ILoveYou virus e.g. "LOVE-LETTER-FOR-YOU.TXT.ps1" – Keith Hill Aug 20 '09 at 22:01

    After digging through the PowerShell documentation, I discovered some useful information about this issue. You can't use the $args if you used the param(...) at the beginning of your file; instead you will need to use $PSBoundParameters. I copy/pasted your code into a PowerShell script, and it worked as you'd expect in PowerShell version 2 (I am not sure what version you were on when you ran into this issue).

    If you are using $PSBoundParameters (and this ONLY works if you are using param(...) at the beginning of your script), then it is not an array, it is a hash table, so you will need to reference it using the key / value pair.

    param($p1, $p2, $p3, $p4)
    $Script:args=""
    write-host "Num Args: " $PSBoundParameters.Keys.Count
    foreach ($key in $PSBoundParameters.keys) {
        $Script:args+= "`$$key=" + $PSBoundParameters["$key"] + "  "
    write-host $Script:args
    

    And when called with...

    PS> ./foo.ps1 a b c d
    

    The result is...

    Num Args:  4
    $p1=a  $p2=b  $p3=c  $p4=d
                    This doesn't account for OP starting his command line with powershell.exe or pwsh. The behavior changes when OP does that.
                        – Eric Hansen
                    Jul 15 at 22:38
                    @EricHansen I don't know what you mean, I get the same result either way: ` Powershell>powershell.exe .\ParamTest.ps1 val1 val2 val3 val4 Num Args:  4  $p1=val1   $p2=val2   $p3=val3   $p4=val4 `
                        – Randall Borck
                    Jul 17 at 18:37
                    @RandallBrock The behavior of the arguments changes for me. If I'm in CMD/batch, and I do something like pwsh .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, it really doesn't like that. On the other hand, if I'm in PowerShell and do .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, that works like I'd expect.  I've heard that this is how it's intended to function due to "security reasons."
                        – Eric Hansen
                    Jul 17 at 23:36
                    @EricHansen I wonder if it is a version thing. For me, pwsh launches 6.2.0, but powershell.exe launches 5.1.17134.858, both of which produce the same results listed: Powershell>pwsh .\ParamTest.ps1 val1 val2 val3 val4 yields: Num Args:  4  $p1=val1   $p2=val2   $p3=val3   $p4=val4
                        – Randall Borck
                    Jul 18 at 17:42
                    @Timo I don't know what you are doing exactly, but param is a language construct, however it must be the first thing in the file. Did you declare a variable or something else before it? More info here: docs.microsoft.com/en-us/powershell/module/…
                        – Randall Borck
                    Sep 4 at 21:00
    

    If you then placed this file under a folder in your PATH, you could call PowerShell scripts like this:

    ps foo 1 2 3
    

    Quoting can get a little messy, though:

    ps write-host """hello from cmd!""" -foregroundcolor green
    

    You may not get "xuxu p1 p2 p3 p4" as it seems. But when you are in PowerShell and you set

    PS > set-executionpolicy Unrestricted -scope currentuser
    

    You can run those scripts like this:

    ./xuxu p1 p2 p3 p4
    
    .\xuxu p1 p2 p3 p4
    
    ./xuxu.ps1 p1 p2 p3 p4
    

    I hope that makes you a bit more comfortable with PowerShell.

    if you want to invoke ps1 scripts from cmd and pass arguments without invoking the script like

    powershell.exe script.ps1 -c test
    script -c test ( wont work )
    

    you can do the following

    setx PATHEXT "%PATHEXT%;.PS1" /m
    assoc .ps1=Microsoft.PowerShellScript.1
    ftype Microsoft.PowerShellScript.1=powershell.exe "%1" %*
    

    This is assuming powershell.exe is in your path

    https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ftype

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

    site design / logo © 2019 Stack Exchange Inc; user contributions licensed under cc by-sa 4.0 with attribution required. rev 2019.9.23.34994