Why isn't for /f working in this batch script? - windows

I'm trying to capture the output of a command in a batch script with for /f. Here is my code:
set RUNPS="powershell -NoProfile -ExecutionPolicy Bypass -Command"
set OLDPATHPS="[Environment]::GetEnvironmentVariable('PATH', 'User')"
for /f %%i in ('%RUNPS% %OLDPATHPS%') do ^
set OLDPATH=%%i
When I run the script from the console, here is the output:
C:\Users\James\Downloads>testscript
' ' is not recognized as an internal or external command,
operable program or batch file.
' ' is not recognized as an internal or external command,
operable program or batch file.
' ' is not recognized as an internal or external command,
operable program or batch file.
...
On the other hand, when I run the command normally I get
C:\Users\James\Downloads>powershell -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('PATH', 'User')"
C:\Users\James\.dnx\runtimes\dnx-clr-win-x86.1.0.0-beta6\bin;C:\Users\James\.dnx\bin;...
Why does this happen with for /f and what can I do to fix it?
EDIT: Thought it may have been an issue with the single quotes being unescaped. Tried using usebackq:
for /f usebackq %%i in (`%RUNPS% %OLDPATHPS%`) do ^
set OLDPATH=%%i
But it looks like I'm getting the same output.

Try this:
#ECHO OFF
SETLOCAL
set "RUNPS=powershell -NoProfile -ExecutionPolicy Bypass -Command"
set "OLDPATHPS=[Environment]::GetEnvironmentVariable('PATH', 'User')"
for /f "delims=" %%i in ('%RUNPS% "%OLDPATHPS%"') DO (
set OLDPATH=%%i
)
SET o
GOTO :EOF
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can safely be used "quoteless".
The required syntax for the command is
powershell -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('PATH', 'User')"
not
"powershell -NoProfile -ExecutionPolicy Bypass -Command" "[Environment]::GetEnvironmentVariable('PATH', 'User')"
The ' ' is not recognized ... message appears to be the consequence of attempting to use the caret. It appears to be invalid as you've used it - I've never seen that ateempted before.
Note that you need the "delims=" to ensure that a user-path-containing-spaces is not tokenised.

Why would you escape the linebreak after do? You should use:
set RUNPS="powershell -NoProfile -ExecutionPolicy Bypass -Command"
set OLDPATHPS="[Environment]::GetEnvironmentVariable('PATH', 'User')"
for /f %%i in ('%RUNPS% %OLDPATHPS%') do (
set OLDPATH=%%i
)
Note that if you want to use variables like OLDPATH inside a for loop (after the do) you have to use setlocal EnableDelayedExpansion at the top of your script, and use ! instead of % for variables inside the loop
EDIT: your powershell currently has this exception:
-Command : The term '-Command' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a
path was included, verify that the path is correct and try again.
At line:1 char:1
-Command [Environment]::GetEnvironmentVariable('PATH', 'User')
~~~~~~~~
CategoryInfo : ObjectNotFound: (-Command:String) [], CommandNot
FoundException
FullyQualifiedErrorId : CommandNotFoundException

Related

Cannot start a ps1 file with BATCH using `call` or `start`

Ive got issues with a batch command that wont start whatever i was to do about it, even using call wouldnt start it.
:choice2
set /P c1="text here [Y/N]? "
if /I "%c1%" EQU "Y" start "" /wait /high /max "Powershell.exe -executionpolicy remotesigned -File %batdir%batch\filename.ps1"
if /I "%c1%" EQU "N" goto :end
goto :choice2
Main requirement is that the %batdir% is in the first if line, because the batch runs from a flash drive.
Tried without %batdir%, wouldnt work either way.
Tried using different commands for the powershell.exe, tried using a direct dir of powershell, wouldnt work, always getting the "Cannot find file, make sure it is typed in properly.".
I would appreciate any help anyone could provide.
As you have not provided a Minimal Complete and Verifiable Example, this assumes that %batdir% was the location of the running batch file, which I've replaced with the appropriate %~dp0 variable. You should also note that I've also replaced your set /p command, because that is not the correct command to use for known single key input:
:choice2
"%SystemRoot%\System32\choice.exe" /M "text here"
If ErrorLevel 2 GoTo end
Start "" /Wait /High /Max "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy RemoteSigned -File "%~dp0batch\filename.ps1"
GoTo choice2
:end
Pause
I do not recommend the use of /High or /Max for this task, (including them only because you did). So because you said you also tried call, it seems to me as if you probably don't need to use start with those options either. If you just need to run the powershell script and wait for it to complete, before returning to the input prompt, try this modification:
:choice2
"%SystemRoot%\System32\choice.exe" /M "text here"
If ErrorLevel 2 GoTo end
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy RemoteSigned -File "%~dp0batch\filename.ps1"
GoTo choice2
:end
Pause

How capture errorlevel of powershell script in a batch file when execute as admin

I need to capture output of powershell script in a batch file when execute as admin.
Example:
ps1 file:
Write-Host "PS1 executed"
exit 1
If I execute powershell script without admin access
NotAdminFile.bat:
#ECHO OFF
setlocal enableextensions
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
echo %ERRORLEVEL%
endlocal
Then, the output is
PS1 executed
1
This is ok.
But, when I execute powershell script with admin access
AdminFile.bat:
#ECHO OFF
setlocal enableextensions
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File "%~dpn0.ps1" ' -Verb RunAs}"
echo %ERRORLEVEL%
endlocal
Then, the output is:
0
I don't want that. Can you help me please?
exit_1_only.ps1
Write-Host "executed: $($MyInvocation.Line)"
# pause
Exit 123+[int]([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::
GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")
q44600354.bat
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
(call )
echo errorlevel clear=%errorlevel%
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
"& 'D:\PShell\tests\exit_1_only.ps1'; exit $LASTEXITCODE"
echo errorlevel non-admin=%errorlevel%
echo(
(call )
echo errorlevel clear=%errorlevel%
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
"& {exit ( Start-Process -Wait -PassThru -FilePath PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command ""D:\PShell\tests\exit_1_only.ps1; exit $LASTEXITCODE"" ' -Verb RunAs).ExitCode}"
echo errorlevel admin=%errorlevel%
Output
==> D:\bat\SO\q44600354.bat
errorlevel clear=0
executed: & 'D:\PShell\tests\exit_1_only.ps1'; exit $LASTEXITCODE
errorlevel non-admin=123
errorlevel clear=0
errorlevel admin=124
==>
Explanation
Exit Codes
In PowerShell $? contains True if last operation succeeded and
False otherwise.
The exit code of the last Win32 executable execution is stored in the
automatic variable $LASTEXITCODE
To read exit codes (other than 0 or 1) launch the PowerShell
script and return the $LASTEXITCODE in a single line like this:
powershell.exe -noprofile C:\scripts\script.ps1; exit $LASTEXITCODE
Start-Process
-PassThru
Returns a System.Diagnostics.Process process object for each process
that the cmdlet started. By default, this cmdlet does not generate any
output.
-Wait
Indicates that this cmdlet waits for the specified process to complete
before accepting more input. This parameter suppresses the command
prompt or retains the window until the process finishes.
(call ): Dave Benham in reply to setting ERRORLEVEL to 0 question:
If you want to force the errorlevel to 0, then you can use this
totally non-intuitive, but very effective syntax: (call ). The space
after call is critical. If you want to set the errorlevel to 1,
you can use (call). It is critical that there not be any space after
call.

Embedding PowerShell in batch

I want to run a few PowerShell commands through a batch file. Very similar question has been asked but I dont want to run a seperate shell file from a batch. Instead I want to embed PowerShell commands to a batch file.
When I try to run
powershell -command "&{$var = "something"}"
I get the following error:
something : The term 'something' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:10
&{$var = something}
CategoryInfo : ObjectNotFound: (something:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
But if I run something like
powershell -command "&{echo "something"}"
Then everything is fine :/
Am I doing a syntax error or something? And please don't give answers like "Instead of using PowerShell commands use batch commands etc..."
Thanks in advance!
You can not use nested quotes when defining the value of a variable as a string. You may either use apostrophes instead of the nested quotes:
powershell -command "&{$var = 'something'; echo $var}"
... or escape the nested quotes:
powershell -command "&{$var = \"something\"; echo $var}"
Another example:
powershell -command "&{$var = 'one'+\" two\"; echo $var}"
Maybe a bit late but I program pure powershell scripts and embed the complete script in a .cmd file to bypass the execution policy setting. I have 2 variants pick the one you like. Both are one-liners that you put on the first line of the .cmd file. Starting from the second line you just program in pure powershell. No need for modifying anything in your powershell script.
Variant1:
#type "%0" | findstr /v "^#type \"%0\" | findstr /v " | PowerShell.exe -noprofile - & goto :eof
Varant2:
#powershell -command "(Get-Content '%0') | select -skip 1 " | powershell -noprofile - & goto :eof

How to run PowerShell in CMD

I'm trying to run a PowerShell script inside cmd command line. Someone gave me an example and it worked:
powershell.exe -noexit "& 'c:\Data\ScheduledScripts\ShutdownVM.ps1'"
But the problem is my PowerShell script has input parameters, so I tried, but it doesn't work:
powershell.exe -noexit "& 'D:\Work\SQLExecutor.ps1 -gettedServerName "MY-PC" ' "
The error is:
The term 'D:\Work\SQLExecutor.ps1 -gettedServerName "MY-PC" ' is not recognized as the name of a cmdlet, function,
How can I fix this problem?
You need to separate the arguments from the file path:
powershell.exe -noexit "& 'D:\Work\SQLExecutor.ps1 ' -gettedServerName 'MY-PC'"
Another option that may ease the syntax using the File parameter and positional parameters:
powershell.exe -noexit -file "D:\Work\SQLExecutor.ps1" "MY-PC"
I'd like to add the following to Shay Levy's correct answer:
You can make your life easier if you create a little batch script run.cmd to launch your powershell script:
run.cmd
#echo off & setlocal
set batchPath=%~dp0
powershell.exe -noexit -file "%batchPath%SQLExecutor.ps1" "MY-PC"
Put it in the same path as SQLExecutor.ps1 and from now on you can run it by simply double-clicking on run.cmd.
Note:
If you require command line arguments inside the run.cmd batch, simply pass them as %1 ... %9 (or use %* to pass all parameters) to the powershell script, i.e.
powershell.exe -noexit -file "%batchPath%SQLExecutor.ps1" %*
The variable batchPath contains the executing path of the batch file itself (this is what the expression %~dp0 is used for). So you just put the powershell script in the same path as the calling batch file.
Try just:
powershell.exe -noexit D:\Work\SQLExecutor.ps1 -gettedServerName "MY-PC"

CMD Return ErrorLevel Without Running Program

I'm running Powershell commands through a CMD prompt and I'd like to check if Powershell is installed first before I start running my commands. I'd like the script to exit if powershell doesn't exist without showing the actual error below. Here's my script:
#echo off
setlocal enabledelayedexpansion
:: Check to see if Powershell is installed
powershell.exe -command {"test"} > NUL
if errorlevel 1 (
echo/Powershell is NOT Installed
EXIT
) else (
goto PSI
)
:PSI
powershell Set-ExecutionPolicy RemoteSigned
The problem that I'm having is that I'm getting this as output:
Powershell is NOT Installed
'powershell.exe' is not recognized as an internal or external command,
operable program or batch file.
Figured it out! I had to use 2>NUL instead of NUL to redirect output:
:: Check to See if Powershell is Installed
powershell.exe test 2>NUL
if errorlevel 1 (
echo/Powershell is NOT Installed
EXIT
) else (
goto PSI
)

Resources