locked
Passing command line args with quotes RRS feed

  • Question

  • Ok, I give up.  How do you pass in a quoted string as a command line argument and get it to retain it's value?

    Here is my sample code:

    $arg = 0
    do 
    {
    	switch -regex ($args[$arg]) 
     { 
       "^-v$"
       {
       	Write-Host "Switch: " $args[$arg]
       	$arg++
       	Write-Host "Value: " $args[$arg]
       }
     }
     
     $arg++
    }
    while ($arg -le ($args.count))
    


    here are three different attempts -

    Try 1: No spaces:
    U:\My Documents\Development\Powershell>powershell .\ping_test.ps1 -v bob

    Switch:  -v
    Value:  bob

    Looks good.

    Try 2: Quoted text with spaces:
    U:\My Documents\Development\Powershell>powershell .\ping_test.ps1 -v "bob ed larry"
    Switch:  -v
    Value:  bob

    Broken.

    Try 3: Quoted Text, comma seperated:
    U:\My Documents\Development\Powershell>powershell .\ping_test.ps1 -v "bob,ed,larry"
    Switch:  -v
    Value:  bob ed larry

    Really broken.

    So what gives?  What does it take to get my string, unmolested, to my script?

    Wednesday, July 20, 2011 12:15 AM

Answers

  • The first set of quotes is consumed by the parser, wrap it in another set of quotes:

     

    ## args.ps1 ##

    for($i=0; $i -lt $args.length; $i++)
    {
     write-host "Arg$i = $($args[$i])"
    }

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


    PS > .\args.ps1 "bob ed larry" "'bob ed larry'" '"bob ed larry"'

    Arg0 = bob ed larry
    Arg1 = 'bob ed larry'
    Arg2 = "bob ed larry"


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar

    Wednesday, July 20, 2011 7:29 AM
  • I call PowerShell scripts from the cmd often, and you can accomplish want you want and more without too much trouble. PowerShell has built-in support for script *parameters* when called from cmd. You just have to do two things:

    1. Use the -file switch when calling PowerShell
    2. Define your script parameters using a param block a the top of the script. Then you can use your parameters from the command line literally as if you were already in a PowerShell session. Many types of parameters are supported: [switch] types, named, positional... You can even use escape characters in your strings. 

    "Triple quotes" isn't so strange as it sounds. Any language requires some delimiter to enclose a string that includes white space. Most languages use the double- or single-quote character. So if you also want to include a quote character in the string itself, you need to do something special--that's also true of just about any programming language. Some languages (including VB and the native CMD interpreter) use two quotes (""). C# and Perl require a backslash (\"). PowerShell natively requires an accent (`"). In all cases it requires two characters to get the one-character double-quote. Incidentally in the example below, I show that when using the -file switch and then PowerShell script parameters, you actually use the backslash escape...just like Perl.

    Check this out:

    param( [Parameter(Mandatory=$true,Position=0)][string]$PositionOrNamed1,
     [Parameter(Mandatory=$true,Position=1)][string]$PositionOrNamed2,
     [Parameter(Mandatory=$false)][switch]$NamedSwitch,
     [Parameter(Mandatory=$false)][string]$NamedString,
     [Parameter(Mandatory=$false)][double]$NamedDouble )
     
    $joined = "{ " + [String]::Join(" , ", $PositionOrNamed2.Split(",")) + " }"
    
    $UsedSwitch = "not "
    if( $NamedSwitch ) { $UsedSwitch = [String]::Empty }
    
    $NamedStringMessage = "NamedString was not passed"
    if( $NamedString -ne [String]::Empty ) { $NamedStringMessage = "NamedString = $NamedString" }
    
    $NamedDoubleMessage = "NamedDouble was not passed"
    if( $NamedDouble -ne $null ) { $NamedDoubleMessage = "NamedDouble = $NamedDouble" }
    
    Write-Host "PositionOrNamed1 = $PositionOrNamed1"
    Write-Host "PositionOrNamed2 = $joined"
    Write-Host "NamedSwitch was $($UsedSwitch)passed"
    Write-Host $NamedStringMessage
    Write-Host $NamedDoubleMessage
    
    

    And calling this from cmd:

    C:\blah>powershell -file FlagsExample.ps1"First" "Second1,Second2" -NamedDouble 1.131312 -NamedSwitch -NamedString "Here is even a string with a quote(\") character *in* it, using the backslash (\) escape character"

    Produces:

    PositionOrNamed1 = First
    PositionOrNamed2 = { Second1 , Second2 }
    NamedSwitch was passed
    NamedString = Here is even a string with a quote(") character *in* it, using the backslash (\) escape character
    NamedDouble = 1.131312

    Or (note even using scientific notation for double):

    C:\blah>powershell -file FlagsExample.ps1 -PositionOrNamed2 "Second1,Second2" -PositionOrNamed1 Not_quoted -NamedDouble 1.12E4

    Produces:

    PositionOrNamed1 = Not_quoted
    PositionOrNamed2 = { Second1 , Second2 }
    NamedSwitch was not passed
    NamedString was not passed
    NamedDouble = 11200

     


    jmh



    Wednesday, July 20, 2011 12:33 PM
  • First, keep a clean language!

    Values passed to to parameters do not require the use of quotes unless they contain spaces (or any other non-valid characters).

    If you want to preserve the values with sorrunding quotes (some legacy applications require that) you have to double them, as the first set of quotes is removed and the inner value is passed over.

     


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar

    Wednesday, July 20, 2011 1:19 PM

All replies

  • You should never really need to parse flags and stuff yourself, it's all built in for you:

     

    function DoStuff
    {
       param(
          [string] $v
       )

       write-host "Value: $v"
    }

    PS>DoStuff 'foo'
    Value: foo
    PS>DoStuff -v unquoted
    Value: unquoted
    PS>DoStuff -v 'with spaces inside'
    Value: with spaces inside

    Do you have a specific scenario where you need to do this manually?

    Thanks,
    -Lincoln


    Edit:  Ok, I think I understand you better now.  You are invoking powershell from cmd.exe, correct?  Didn't realize that at first.  The quote parsing in cmd is a giant pain in the rear.  Go ahead and *triple* quote those bad boys, and it should work ok...
    • Proposed as answer by Michael Bast Tuesday, August 28, 2018 9:37 AM
    Wednesday, July 20, 2011 12:29 AM
  • Triple quote? Are you f'in kidding me M$FT???

    Yeah so that works.  Man the dark empire really knows how to build a robust scripting language.  HAHA Good to see they are about 20 years behind Perl.  Which, coincidentally, in the time I was waiting for an answer here I rewrote the tool in and thankfully it just works simply. And without triple quotes LOL.

    Thanks for the feedback. 

    Wednesday, July 20, 2011 12:47 AM
  • The first set of quotes is consumed by the parser, wrap it in another set of quotes:

     

    ## args.ps1 ##

    for($i=0; $i -lt $args.length; $i++)
    {
     write-host "Arg$i = $($args[$i])"
    }

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


    PS > .\args.ps1 "bob ed larry" "'bob ed larry'" '"bob ed larry"'

    Arg0 = bob ed larry
    Arg1 = 'bob ed larry'
    Arg2 = "bob ed larry"


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar

    Wednesday, July 20, 2011 7:29 AM
  • I call PowerShell scripts from the cmd often, and you can accomplish want you want and more without too much trouble. PowerShell has built-in support for script *parameters* when called from cmd. You just have to do two things:

    1. Use the -file switch when calling PowerShell
    2. Define your script parameters using a param block a the top of the script. Then you can use your parameters from the command line literally as if you were already in a PowerShell session. Many types of parameters are supported: [switch] types, named, positional... You can even use escape characters in your strings. 

    "Triple quotes" isn't so strange as it sounds. Any language requires some delimiter to enclose a string that includes white space. Most languages use the double- or single-quote character. So if you also want to include a quote character in the string itself, you need to do something special--that's also true of just about any programming language. Some languages (including VB and the native CMD interpreter) use two quotes (""). C# and Perl require a backslash (\"). PowerShell natively requires an accent (`"). In all cases it requires two characters to get the one-character double-quote. Incidentally in the example below, I show that when using the -file switch and then PowerShell script parameters, you actually use the backslash escape...just like Perl.

    Check this out:

    param( [Parameter(Mandatory=$true,Position=0)][string]$PositionOrNamed1,
     [Parameter(Mandatory=$true,Position=1)][string]$PositionOrNamed2,
     [Parameter(Mandatory=$false)][switch]$NamedSwitch,
     [Parameter(Mandatory=$false)][string]$NamedString,
     [Parameter(Mandatory=$false)][double]$NamedDouble )
     
    $joined = "{ " + [String]::Join(" , ", $PositionOrNamed2.Split(",")) + " }"
    
    $UsedSwitch = "not "
    if( $NamedSwitch ) { $UsedSwitch = [String]::Empty }
    
    $NamedStringMessage = "NamedString was not passed"
    if( $NamedString -ne [String]::Empty ) { $NamedStringMessage = "NamedString = $NamedString" }
    
    $NamedDoubleMessage = "NamedDouble was not passed"
    if( $NamedDouble -ne $null ) { $NamedDoubleMessage = "NamedDouble = $NamedDouble" }
    
    Write-Host "PositionOrNamed1 = $PositionOrNamed1"
    Write-Host "PositionOrNamed2 = $joined"
    Write-Host "NamedSwitch was $($UsedSwitch)passed"
    Write-Host $NamedStringMessage
    Write-Host $NamedDoubleMessage
    
    

    And calling this from cmd:

    C:\blah>powershell -file FlagsExample.ps1"First" "Second1,Second2" -NamedDouble 1.131312 -NamedSwitch -NamedString "Here is even a string with a quote(\") character *in* it, using the backslash (\) escape character"

    Produces:

    PositionOrNamed1 = First
    PositionOrNamed2 = { Second1 , Second2 }
    NamedSwitch was passed
    NamedString = Here is even a string with a quote(") character *in* it, using the backslash (\) escape character
    NamedDouble = 1.131312

    Or (note even using scientific notation for double):

    C:\blah>powershell -file FlagsExample.ps1 -PositionOrNamed2 "Second1,Second2" -PositionOrNamed1 Not_quoted -NamedDouble 1.12E4

    Produces:

    PositionOrNamed1 = Not_quoted
    PositionOrNamed2 = { Second1 , Second2 }
    NamedSwitch was not passed
    NamedString was not passed
    NamedDouble = 11200

     


    jmh



    Wednesday, July 20, 2011 12:33 PM
  • First, keep a clean language!

    Values passed to to parameters do not require the use of quotes unless they contain spaces (or any other non-valid characters).

    If you want to preserve the values with sorrunding quotes (some legacy applications require that) you have to double them, as the first set of quotes is removed and the inner value is passed over.

     


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar

    Wednesday, July 20, 2011 1:19 PM
  • Double quotes don't work. Triple quotes or nested apostrophes do.

    This: psscript.ps1 "19 data" gets '19' as first arg and 'data' as second arg, even with param def. Tried it on Windows 10.

    This: psscript.ps1 "'19 data'" works (apostrophes inside quotes), as does """19 data""" (strange but true).

    This is because *Powershell* loses the fact that the shell passes a normally quoted argument correctly.

    Here's what Perl thinks:

    >perl -e "$i=-1;foreach(@ARGV){$i++;print qq(arg $i is >$ARGV[$i]<\n);}" one two "t h r e e" "'fo ur'" """fi ve"""
    arg 0 is >one<
    arg 1 is >two<
    arg 2 is >t h r e e<
    arg 3 is >'fo ur'<
    arg 4 is >"fi<
    arg 5 is >ve"<
    

    This is what Powershell (on Windows 10 at least) thinks:

    >powershell /command "echo $args" one "t w o" "'th ree'" ""fo ur"" """fi ve"""
    one
    t
    w
    o
    th ree
    fo
    ur
    fi ve
    
    

    So I suppose Powershell is "fixing up" the """fi ve""" case.



    Thursday, April 12, 2018 6:18 PM
  • I am running into a related problem.

    I am helping figure this out for another developer who has written an extension for Visual Studio Code. It was pointed out to him by myself that spaces in paths and file names in his extension cause errors. So we have been trying to fix it together.

    His extension runs a custom command when you select to 'build' the file being edited. With some help from me he has made it work in almost all scenarios. The one causing us problems is when the custom command is executed under PowerShell with spaces in a custom output path (can be set in the extension settings by the user). I have done extensive testing and research to find out the issue and figured out what is going wrong but have no idea how to fix it.

    The problem is that one of the arguments for the command to set the custom output path uses this format:

    Output_File_Name=<path>

    If the path contains spaces it needs to be wrapped in quotation marks, E.G.:

    Output_File_Name='D:\My Output Files\Example File.OUT'

    For some reason PowerShell is passing this instead:

    "Output_File_Name=D:\My Output Files\Example File.OUT"

    This causes the command to fail.

    I have tried many methods of passing the argument to the command but all give the same result of the whole argument being wrapped in double quotes because it contains spaces! I only want the path be contained in quotes, not the whole argument. I am not a PowerShell user so this is new to me and I may be missing something obvious. I have tried all the usual tricks of triple quotes or using strings but all do the same.

    Thursday, May 9, 2019 1:02 AM