Execute PowerShell command from cmd - quotes issue - powershell

I have to execute two commands (including a PowerShell one) in new console window. Let me explain:
I am able to execute a PowerShell command from cmd in a new console window (I can specify the command inside quotes):
start /W cmd /K powershell -command "&{Write-Host 'Hello World!';}"
And I can execute two commands (I can write both commands in quotes):
start /W cmd /K "echo Hello && echo World"
But how can I do it together (execute two commands, one of them PowerShell)? I would like to do it without any .bat or .ps script.
I tried something like following (and some variations with escaping character), but I cannot deal with this quotes issue (there are quotes for joining two commands and inside quotes for PowerShell command).
start /W cmd /K "echo Hello && powershell -command "&{Write-Host 'Hello World!';}""

You need to escape & in powershell's ScriptBlock for cmd:
start /W cmd /K "echo Hello && powershell -command "^&{Write-Host 'Hello World!';}""

Related

Automate creation of symbolic links on Windows bash

I'm trying to make a script that will do some directory management. The final script will run on Windows and will preferably be written in python. At one point in the script I need to automate the creation of multiple symbolic links between multiple folders. The script itself runs without administrator permissions from a bash terminal (Git Bash). Windows is not in developer mode.
The perfect solution would be to have a list of tuples (link, source) and create the corresponding symbolic links all at once, while having to press "Yes" for administrator rights only once.
I already did some research:
How to create a symlink between directories from inside an elevated cmd: Git Bash shell fails to create symbolic links
mklink /D link source_directory
How to run a command in cmd as an administrator from inside bash: Launch Elevated CMD.exe from Powershell
powershell 'start cmd -v runAs -Args /k, [comma-separated-args]'
How to set the working directory after launching the powershell command as an administrator (Otherwise it launches a terminal from inside C:\Windows\System32\): PowerShell: Run command from script's directory
powershell 'start cmd -v runAs -Args /k, cd, $pwd, "&", [comma-separated-args]'
Let's say I want to create a symbolic link in my current working directory to a relative directory. I tried 2 ways:
When I combine all of the above points and execute the following command from the Git Bash terminal:
powershell 'start cmd -v runAs -Args /k, cd, $pwd, "&", mklink, /D, \"link_to_utils\", \"common\utils\"'
A new terminal opens up (after agreeing for admin rights). But it resulted in a new symlink being created in the root of C:\ .
When I execute this:
powershell 'start cmd -v runAs -Args /k, cd, $pwd
A new terminal opens up (after agreeing for admin rights). I can now run this command:
mklink /D "link_to_utils" "common\utils"
The link is created in the current working directory, as I wanted.
So my questions are:
a) How can I make option 1 work in bash?
b) Why is it actually creating the symlink in C:\?
c) Is there a way to pipe a command into the opened elevated cmd terminal (to make option 2 work)?
Note: I have been trying to find a solution using python and the win32api (pywin32). But that resulted in a bunch of command prompts opening up for each symlink that needs to be created. Also there is barely any documentation regarding pywin32.
Use the following:
powershell 'Start-Process -Verb RunAs cmd /k, " cd `"$PWD`" & mklink /D `"link_to_utils`" `"common\utils`" "'
Since it is PowerShell that interprets that verbatim content of the command line being passed, its syntax rules must be followed, meaning that a "..." (double-quoted) string is required for expansion (string interpolation) of the automatic $PWD variable to occur, and that embedded " characters inside that string must be escaped as `" ("" would work too).
The pass-through command line for cmd.exe is passed as a single string argument, for conceptual clarity.

how to run several cmd command prompts from powershell

So I am trying to write a script that allows me to open several cmd command prompt and write in them the same command but with different variables.
The solution that I came with was to write a PowerShell script that calls inside a loop a cmd file and pass a variable each time to the cmd file but I'm stuck, the PowerShell script execute only one cmd.
Can someone help to figure this out ?
Thanks :)
You can use the following :
cmd.exe /c [command]
for example
$x = 1..100
foreach ($n in $x){cmd.exe /c ping 192.168.1.$n}
mohamed saeed's answer shows you to execute cmd.exe commands synchronously, in sequence in the current console window.
If, by contrast, you want to open multiple interactive cmd.exe sessions, asynchronously, each in its separate, new window, use cmd.exe's /k option and invoke cmd.exe via Start-Process:
# Open three new interactive cmd.exe sessions in new windows
# and execute a sample command in each.
'date /t', 'ver', "echo $PSHOME" | ForEach-Object {
# Parameters -FilePath and -ArgumentList implied.
Start-Process cmd.exe /k, $_
}
Note:
Unless your cmd.exe console windows have the Let the system position the window checkbox in the Properties dialog checked by default, all new windows will fully overlap, so that you'll immediately only see the last one opened.
The last new window opened will have the keyboard focus.
The commands passed to /k are instantly executed, but an interactive session is then entered.
If you would like to keep in purely batch, you can use the start command. The /k switch keeps the command line open. You would use /c if you want to carry out the command and terminate :
start "" %comspec% /k ping 192.168.1.1
From powershell, you can use the Start-Process command with an ArgumentList:
Start-Process cmd.exe -ArgumentList "/k ping 192.168.1.1"

Multiple inputs into new prompt & Powershell -run as and -nonewwindow issue

Here is what I currently do, file 1:
powershell.exe -command "Start-Process cmd -ArgumentList '/c cd C:\ && DiskZero.cmd'-Verb runas"
And file 2 "DiskZero.cmd":
#echo off
(echo rescan
echo sel disk 1
echo cle all
echo cre part prim
echo for fs=ntfs quick label=Intenso
echo assign letter=E
) | diskpart
pause
It works as intended, however, there is two files, what I want to do is make it so there's only one file.
I can't manage to find how to input multiple lines of code into a new elevated command prompt with only one script, so instead I'm trying to do it with powershell:
start cmd -nonewwindow works
start cmd -ver runas works
however start cmd -nonewwindow -ver runas doesn't work
What I was hoping to do was this in powershell:
start cmd -nonewwindow -ver runas
#echo off
(echo rescan
echo sel disk 1
echo cle all
echo cre part prim
echo for fs=ntfs quick label=Intenso
echo assign letter=E
) | diskpart
pause
Can anyone help me solve the start cmd -nonewwindow -ver runas issue OR input multiple lines of code into a new elevated command prompt with only one file, please?
Can anyone help me solve the start cmd -nonewwindow -verb runas issue
Unfortunately, there is no solution: Windows fundamentally does not allow you to run an elevated process (run as admin, requested with -Verb RunAs) directly in a non-elevated process' console window - that is why Start-Process syntactically prevents combining -NoNewWindow with -Verb RunAs.
OR input multiple lines of code into a new elevated command prompt with only one file, please?
While there is a solution, it'll be hard to maintain:
You can pass the lines of your second batch file (the one you want to eliminate) to cmd /c on a single line, joined with &:
Note: To facilitate side effect-free experimentation, the original diskpart command was replaced with findstr -n ., which merely prints the lines received via stdin, preceded by their line number.
powershell.exe -command "Start-Process -Verb RunAs cmd '/c cd C:\ && (echo rescan&echo sel disk 1&echo cle all&echo cre part prim&echo for fs=ntfs quick label=Intenso&echo assign letter=E) | findstr -n .&pause'"
That no space char. precedes each & is deliberate, because trailing whitespace in echo commands is significant, i.e. it becomes part of the output; however, it should be fine to place a space char. after each & (as well as before, if the preceding command ignores trailing whitespace).
A better solution is to create a temporary helper batch file from your batch file, pass its path to the PowerShell command, and delete it afterwards:
#echo off
:: Determine the path for a temporary batch file...
:: Note: %~snx0 uses the short (8.3) name of the batch file, so as
:: to ensure that the temp. file path has no spaces, which
:: obviates the need for complex double-quoting later.
set "tmpBatchFile=%TEMP%\~%~snx0"
:: ... and fill it with the desired commands.
:: Note how metacharacters - ( ) | ... - must be ^-escaped.
(
echo #echo off
echo ^(echo rescan
echo echo sel disk 1
echo echo cle all
echo echo cre part prim
echo echo for fs=ntfs quick label=Intenso
echo echo assign letter=E
echo ^) ^| findstr -n .
echo pause
) > "%tmpBatchFile%"
:: Now you can let the elevated cmd.exe process that PowerShell launches
:: execute the temp. batch file.
:: Note: -Wait ensures that the PowerShell call blocks until the elevated
:: cmd.exe window closes.
powershell.exe -command "Start-Process -Wait -Verb RunAs cmd '/c cd C:\ & %tmpBatchFile%'"
:: Delete the temp. batch file.
:: Note: If you do NOT use -Wait above, you'll have to defer deleting
:: the batch file until after the elevated cmd.exe window closes,
:: which you'll have to do manually.
del "%tmpBatchFile%"

PSExec and Powershell fails to run a program located in Program Files (x86)

I am struggling to use Psexec inside of a PS script to execute an interactive program.
I have tried this:
PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell notepad" 2> $null
... and it runs perfectly fine. Notepad is launched on a remote machine. Now, when I want to run .exe from Program Files (x86) I get absolutely nothing.
I have tried this variations to run 1.exe located in ProgramFiles(x86):
PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell "${env:ProgramFiles(x86)}\1.exe"" 2> $null
PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell "${env:ProgramFiles(x86)}" + "\1.exe"" 2> $null
However none of them work. Any idea what´s wrong?
Try the following:
psexec cmd /c 'echo . | powershell "& \"${env:ProgramFiles(x86)}\1.exe\"' 2>$null
Note: To better focus on the fundamentals of the solution, I've simplified the psexec command, but the original command should work too.
The entire string passed to cmd /k is single-quoted to prevent PS from interpolating elements up front, notably ${env:ProgramFiles(x86)} whose expansion should be deferred until the command is executed on the target machine.
Note that you normally need a double-quoted string when you pass a command line to cmd /c when invoking from cmd.exe itself. From within PowerShell, however, this is not a requirement: PowerShell first parses the string - whether single- or double-quoted originally - interpolates, if applicable, and then passes the resulting string double-quoted to the external command.
Note the & \"...\" construct in the context of the powershell argument, which ensures that the path with embedded spaces is correctly executed.
Curiously, PS requires " chars. to be escaped as \" when a parameter is passed from the outside world (as opposed to escaping as `" inside the realm of PS).
The command passed to powershell as a whole must be double-quoted, because cmd.exe - in whose context powershell is invoked due to cmd /c - only recognizes double quotes as parameter delimiters and only double quotes protect the enclosed content (mostly) from interpretation.
Why your commands didn't work:
The primary problem was that the executable path that you wanted powershell.exe to invoke ended up containing spaces (C:\Program Files...), causing PowerShell not to recognize the entire path as a single argument. Such a path must be (a) quoted and (b) invoked with &, the call operator.
(In the 2nd attempt, with + ... (string concatenation), you would have had to use & also, and enclose the concatenation in (...)).
For debugging, using cmd /k instead of cmd /c can give you a better sense of how the command is ultimately executed (/k keeps the console window open after execution of the command).
A subtler point is that by using a double-quoted string overall, ${env:ProgramFiles(x86)} was expanded on the source machine rather than on the target machine, where the definition of that environment variable may or may not be the same.
You're putting yourself in Escape Hell by mixing PowerShell, CMD and PsExec. If all you want is run an executable on a remote host, just stick with CMD and PsExec (run the command from CMD too):
PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c echo. ^| "%ProgramFiles(x86)%\1.exe" 2>nul
That way you just need to escape the pipe (^|) and put the path with spaces in double quotes.

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.