How do I pass in a string with spaces into PowerShell? - powershell

Given:
# test1.ps1
param(
$x = "",
$y = ""
)
&echo $x $y
Used like so:
powershell test.ps1
Outputs:
> <blank line>
But then this goes wrong:
test.ps1 -x "Hello, World!" -y "my friend"
Outputs:
Hello,
my
I was expecting to see:
Hello, World! my friend

Well, this is a cmd.exe problem, but there are some ways to solve it.
Use single quotes
powershell test.ps1 -x 'hello world' -y 'my friend'
Use the -file argument
powershell -file test.ps1 -x "hello world" -y "my friend"
Create a .bat wrapper with the following content
#rem test.bat
#powershell -file test.ps1 %1 %2 %3 %4
And then call it:
test.bat -x "hello world" -y "my friend"

One can use a backtick ` to escape spaces:
PS & C:\Program` Files\\....

A possible solution was in my case to nest the single and the double quotes.
test.ps1 -x '"Hello, World!"' -y '"my friend"'

I had a similar problem but in my case I was trying to run a cmdlet, and the call was being made within a Cake script. In this case the single quotes and -file argument did not work:
powershell Get-AuthenticodeSignature 'filename with spaces.dll'
Resulting error: Get-AuthenticodeSignature : A positional parameter cannot be found that accepts argument 'with'.
I wanted to avoid the batch file if possible.
Solution
What did work was to use a cmd wrapper with /S to unwrap outer quotes:
cmd /S /C "powershell Get-AuthenticodeSignature 'filename with spaces.dll'"

Related

variable doesn't get passed in PowerShell script

I have a simple PowerShell script (.ps1 file, detailed below) and I'm trying to pass a variable that needs to be present in the window title and also a log file name. The script runs, but the places where the variable's string is suppose to be erroneously shows up blank (i.e., "session-" and "log-20210612_155506-session-.log"). What is the correct way to pass this variable??
$mysessionID = [guid]::NewGuid().toString().ToLower()
invoke-expression 'cmd /c start powershell -NoExit -Command {cd .\;$host.ui.RawUI.WindowTitle = "session-$mysessionID"; start-sleep 0 ; .\chia_plot.exe -p 987d987987987fd879879 -f 9x79879f987987sd -t D:\ -d R:\ -n -1 -r 4 -u 128 | Tee-Object -FilePath ".\logs\log-$(get-date -f yyyyMMdd_HHmmss)-session-$mysessionID.log"}'
The problem is that you start the string passed to Invoke-Expression with a single quote. In Powershell, the single quote does not allow string interpolation, but the double quote does. You should change the Invoke-Expression call to start with double quotes, but this will require that you change the inner double quotes to single quotes. For example:
Invoke-Expression "cmd /c start powershell -NoExit -Command { cd .\; $host.ui.RawUI.WindowTitle = 'session-$mysessionID'; start-sleep 0; .\chia_plot.exe -p 987d987987987fd879879 -f 9x79879f987987sd -t D:\ -d R:\ -n -1 -r 4 -u 128 | Tee-Object -FilePath '.\logs\log-$(get-date -f yyyyMMdd_HHmmss)-session-$mysessionID.log' }"

passing a second parameter in a batch file

I have a file hello.bat with the following code:
echo first: %1 and second: %2 > me.txt
I am trying to call this using powershell. When I pass the first parameter it works well:
start-process hello test
However when i try to pass the second parameter like this:
start-process hello test test2
I get this error:
Start-Process : A positional parameter cannot be found that accepts argument 'test2'
You can just try pass the arguments using a comma like this:
start-process hello test,test2
or
start-process hello "test test2"
If you want to know more you can read the documentation here.
I don't know why you're starting a command line from a command line but I think this should work:
Start-Process -FilePath $env:ComSpec -ArgumentList "/c hello.bat test test2"
Don't you think it's time to replace cmd with Powershell?
You might change your bat file as well:
echo %1 > me.txt
echo %2 >> me.txt

How to run a command correct for CMD in remote box using PowerShell?

I need to run a remote command with help of PowerShell from CMD. This is the command I call from CMD:
powershell -command "$encpass=convertto-securestring -asplaintext mypass -force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass; invoke-command -computername "REMOTE_COMPUTER_NAME" -scriptblock {<command>} -credential $cred;"
in place of <command> (including < and > signs) can be any command which can be run in cmd.exe. For example there can be perl -e "print $^O;" or echo "Hello World!" (NOTE: There cannot be perl -e 'print $^O;', because it is incorrect command for CMD due to the single quotes). So it appears the command perl -e "print $^O;" and any other command which contains double quotes doesn't handled as expected. Here I expect it to return OS name of remote box from perl's point of view, but it prints nothing due to obscure handling of double quotes by PowerShell and/or CMD.
So the question is following, how to run command correct for CMD in remote box using PowerShell?
There are several possible problems with the command line in the OP. If the command line in the OP is being executed from Powershell itself the $encpass and $cred will get substituted before the (sub-instance) of powershell is invoked. You need to use single quotes or else escape the $ signs, for example:
powershell -command "`$encpass=2"
powershell -command '$encpass=2'
If, instead of using Powershell, the command line is executed from CMD, then ^ has to be escaped, because it is the CMD escape character.
And quoting " is a good idea as well. In a few tests that I did I had to use unbalanced quotes to get a command to work, for example, from powershell:
powershell -command "`$encpass=`"`"a`"`"`"; write-host `$encpass"
worked, but balanced quotes didn't.
To avoid all this, probably the most robust way to do this is given in powershell command line help: powershell -?:
# To use the -EncodedCommand parameter:
$command = 'dir "c:\program files" '
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encodedCommand
However there is a new feature in PS 3.0 that is also supposed to help, but I don't think it will be as robust. Described here: http://blogs.msdn.com/b/powershell/archive/2012/06/14/new-v3-language-features.aspx, near the middle of the blog.

powershell sending multiple parameter to a external command

I am trying to run a external exe from a powershell script.
This exe wants 4 parameters.
I have been trying every combo of invoke-item, invoke-command, & 'C:\program files\mycmd.exe myparam', made a shortcut in C:\ to get rid of the spaces in the path.
I can make it work with one parameter, but not with more. I get various errors.
To sum up, how do you send 4 parameters to an exe?
It's best if shown in longhand. Once you see what's going on, you can shorten it down by just using commas between each argument.
$arg1 = "filename1"
$arg2 = "-someswitch"
$arg3 = "C:\documents and settings\user\desktop\some other file.txt"
$arg4 = "-yetanotherswitch"
$allArgs = #($arg1, $arg2, $arg3, $arg4)
& "C:\Program Files\someapp\somecmd.exe" $allArgs
... shorthand:
& "C:\Program Files\someapp\somecmd.exe" "filename1", "-someswitch", "C:\documents and settings\user\desktop\some other file.txt", "-yetanotherswitch"
In the easy case, passing arguments to a native exe is as simple as using a built-in command:
PS> ipconfig /allcompartments /all
You can run into problems when you specify a full path to an EXE and that path contains spaces. For example if PowerShell sees this:
PS> C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\sn.exe -k .\pubpriv.snk
It interprets the command to be "C:\Program" and "Files\Microsoft" as the first parameter, "SDKs\Windows\v7.0\Bin\sn.exe" as the second parameter, etc. The simple solution is to put the path in a string use the invocation operator & to invoke the command named by the path e.g.:
PS> & 'C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\sn.exe' -k .\pubpriv.snk
The next area we run into problems with is when the arguments are complex and/or use characters that PowerShell interprets specially e.g.:
PS> sqlcmd -v user="John Doe" -Q "select '$(user)' as UserName"
This doesn't work and we can debug this by using a tool from the PowerShell Community Extensions called echoargs.exe which shows you exactly how the native EXE receives the arguments from PowerShell.
PS> echoargs -v user="John Doe" -Q "select '$(user)' as UserName"
The term 'user' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, ...
<snip>
Arg 0 is <-v>
Arg 1 is <user=John Doe>
Arg 2 is <-Q>
Arg 3 is <select '' as UserName>
Note that with Arg3 $(user) is interpreted & evaluated by PowerShell and results in an empty string. You can fix this problem and a good number of similar issues by using single quotes instead of double qoutes unless you really need PowerShell to evaluate a variable e.g.:
PS> echoargs -v user="John Doe" -Q 'select "$(user)" as UserName'
Arg 0 is <-v>
Arg 1 is <user=John Doe>
Arg 2 is <-Q>
Arg 3 is <select $(user) as UserName>
If all else fails, use a here string and Start-Process like so:
PS> Start-Process echoargs -Arg #'
>> -v user="John Doe" -Q "select '$(user)' as UserName"
>> '# -Wait -NoNewWindow
>>
Arg 0 is <-v>
Arg 1 is <user=John Doe>
Arg 2 is <-Q>
Arg 3 is <select '$(user)' as UserName>
Note if you are using PSCX 1.2 you will need to prefix Start-Process like so - Microsoft.PowerShell.Management\Start-Process to use PowerShell's built-in Start-Process cmdlet.

Powershell Command Processing (Passing in Variables)

I'm creating a Powershell script to deploy some code and part of the process is to call a command-line compression tool called RAR.EXE to back-up some folders.
I'm attempting to dynamically build out the parameters and then have powershell call the command with the variables but I'm running into trouble. It isn't working...
Run the following script and you should see what I'm talking about. The parameters being passed in as a variable are being mangled. If I pass the entire command + parameters in I get the infamous "is not recognized as a cmdlet..." message.
Thanks for any help!
echo "this should succeed"
& cmd /c echo foo
echo "why does this echo out an additional double quote?"
$param = "/c echo foo"
& cmd "$param"
echo "this does the same"
$param = "/c echo foo"
& cmd $param
echo "escaping the slash doesn't work either..."
$param = "`/c echo foo"
& cmd $param
echo "this fails, but why?"
$cmd = "cmd /c echo foo"
&$cmd
The call operator '&' is unnecessary in this case. It is used to invoke a command in a new scope. This is typically used to invoke a command specified by a string or scriptblock. It also has the side benefit that any variables created in say a PowerShell script are discarded after the command finishes and the scope goes away.
However since the cmd is an EXE it executes in a completely different process. FWIW, you get similar output directly from cmd.exe:
> cmd "/c echo foo"
foo"
So the extra quote on the end is a cmd.exe issue. Typically you need to keep the command separate from the parameters when PowerShell is doing the parsing to invoke the command e.g.
45> & { $foo = "foo" }
46> $foo # Note that $foo wasn't found - it went away with the scope
47> . { $foo = "foo" } # dotting executes in the current scope
48> $foo
foo
The notable exception here is that Invoke-Expression behaves like an "evaluate this string" function. Use with care, especially if the user provides the string. Your day could suck if they provided "ri C:\ -r".
In this case, as others have suggested I would pull the /c out of the string $param string and specify it e.g.:
cmd /c $param
Or use Invoke-Expression but use with care. BTW when you are trying to debug issues with sending arguments to EXE from PowerShell, check out the echoargs utility in PowerShell Community Extensions (http://pscx.codeplex.com). It is very handy:
49> $param = "/c echo foo"
50> echoargs $param
Arg 0 is </c echo foo>
This shows that cmd.exe receives "/c echo foo" as a single argument. "/c" should be a separate argument from "echo foo" (the command to execute).
I have had problems with the & call operator in the past when trying to invoke executable type commands like you are trying. Not sure I understand why. Invoke-Expression however, always seems to work in this context:
PS C:\> $cmd = "cmd /c echo foo"
PS C:\> Invoke-expression $cmd
foo
One other way I found to do this was to create an array of arguments for the command line and use that with the apersand & call operator. Something like this:
$exe = "cmd";
[Array]$params = "/c", "echo", "foo";
& $exe $params;
It's worked well for me.
I originally found this technique here:
http://techstumbler.blogspot.com/2009/12/windows-commands-with-arguments-in.html
Your last example if failing because "&" treats the string as one argument, so it is looking for a program named "cmd /c echo foo.exe". :)
This works:
& $cmd $params
As for the double quote issue, it does seem that cmd does not like the quotes around arguments that PowerShell puts. It gets this:
cmd "/c echo foo"
So I think it treats everything after /c as the exact command, so like so:
echo foo"
Some command line programs and funky parsing of the command line (that is why PowerShell takes over this job for functions and cmdlets). In the case of cmd, I would suggest this:
$param = "echo foo"
& cmd /c $param
it's an artifact of using cmd /c, I think. running
$param = "echo foo"
cmd /c $param
works fine. Unless you have a real code example, it's a bit hard to trouble shoot.
Args are treated differently when they are contained in a String:
PS D:\> echo "1 2 3"
1 2 3
PS D:\> echo 1 2 3
1
2
3
The same results occur when you use a variable for the args:
PS D:\> $param = "1 2 3"
PS D:\> echo $param
1 2 3
The SOLUTION is to use an Array:
PS D:\> $param = #(1,2,3)
PS D:\> echo $param
1
2
3