powershell vs cmd regarding not closing the cmd/powershell window after execution - windows

I have a ipsxe-comp-vars.bat batch file which sets environment variables for the intel fortran compiler, as well as for the c and c++ compilers. I create a shortcut to it, I right-click the shortcut, go to target and put the path of cmd.exe and a /K (such that the command prompt won't close) and a space before the path to the .bat in it, click on apply and ok. Then I can pin the shortcut to the taskbar. When I click on it : the bat is executed and at the end, I am back to the command prompt, the cmd window don't close. I can then start compiling etc in this command windows.
At the same place as the .bat file I created a ipsxe-comp-vars.ps1 file :
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
Set-Location "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\bin"
& "C:\Windows\System32\cmd.exe" /E:ON /V:ON "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\bin\ipsxe-comp-vars.bat" intel64 vs2015
Then I create a shortcut for this .ps1 file, right-click the shortcut, and modify it's target as follows :
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noexit - command "& 'C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\bin\ipsxe-comp-vars.ps1'"
Then I can also pin this new shortcut to the taskbar. When I click on it : the .ps1 file is executed, but the window closes, annihilating the leverage of the very notion of shorcut in this case.
What am I doing wrong ?

Try the following which is how my shortcuts with -noexit are set up.
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -noexit -File "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\bin\ipsxe-comp-vars.ps1"
-Command
Executes the specified commands (and any parameters) as though they were typed at the PowerShell command prompt, and then exits, unless the NoExit parameter is specified. Essentially, any text after -Command is sent as a single command line to PowerShell (this is different from how -File handles parameters sent to a script).
-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 and their values.
You can include the parameters of a script, and parameter values, in the value of the File parameter. For example: -File .\Get-Script.ps1 -Domain Central Note that parameters passed to the script are passed as literal strings (after interpretation by the current shell). For example, if you are in cmd.exe and want to pass an environment variable value, you would use the cmd.exe syntax: powershell -File .\test.ps1 -Sample %windir% If you were to use PowerShell syntax, then in this example your script would receive the literal "$env:windir" and not the value of that environmental variable: powershell -File .\test.ps1 -Sample $env:windir
Typically, the switch parameters of a script are either included or omitted. For example, the following command uses the All parameter of the Get-Script.ps1 script file: -File .\Get-Script.ps1 -All
https://learn.microsoft.com/en-us/powershell/scripting/core-powershell/console/powershell.exe-command-line-help?view=powershell-6

Related

How can I get PowerShell current location every time I open terminal from file explorer

I can open PowerShell window in any directory using Windows File Explorer.
I want to run a script every time a new PowerShell window is open and use current directory where it was open in the script.
Using $profile let me for automatic script execution but $pwd variable does not have directory used to open PowerShell window but has C:\WINDOWS\system32. I understand PowerShell starts in C:\WINDOWS\system32, run $profile and next change location used with File Explorer. How can I get file explorer current directory it when my script is executes from $profile or maybe there is another way to automatic execute my script after PowerShell window is open?
Note: The answer below provides a solution based on the preinstalled File Explorer shortcut-menu commands for Window PowerShell.
If modifying these commands - which requires taking ownership of the registry keys with administrative privileges - or creating custom commands is an option, you can remove the NoWorkingDirectory value from the following registry keys (or custom copies thereof):
HKEY_CLASSES_ROOT\Directory\shell\Powershell
HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
Doing so will make the originating folder the working directory before PowerShell is invoked, so that $PROFILE already sees that working directory, as also happens when you submit powershell.exe via File Explorer's address bar.[1]
Shadowfax provides an important pointer in a comment on the question:
When you hold down Shift and then invoke the Open PowerShell window here shortcut-menu command on a folder or in the window background in File Explorer, powershell.exe is initially started with C:\Windows\System32 as the working directory[1], but is then instructed to change to the originating folder with a Set-Location command passed as a parameter; e.g., a specific command may look like this:
"PowerShell.exe" -noexit -command Set-Location -literalPath 'C:\Users\jdoe'
As an aside: The way this shortcut-menu command is defined is flawed, because it won't work with folder paths that happen to contain ' chars.
At the time of loading $PROFILE, C:\Windows\System32 is still effect, because any command passed to -command isn't processed until after the profiles have been loaded.
If you do need to know in $PROFILE what the working directory will be once the session is open, use the following workaround:
$workingDir = [Environment]::GetCommandLineArgs()[-1] -replace "'"
[Environment]::GetCommandLineArgs() returns the invoking command line as an array of arguments (tokens), so [-1] returns the last argument, assumed to be the working-directory path; -replace "'" removes the enclosing '...' from the result.
However, so as to make your $PROFILE file detect the (ultimately) effective working directory (location) irrespective of how PowerShell was invoked, more work is needed.
The following is a reasonably robust approach, but note that a fully robust solution would be much more complex:
# See if Set-Location was passed and extract the
# -LiteralPath or (possibly implied) -Path argument.
$workingDir = if ([Environment]::CommandLine -match '\b(set-location|cd|chdir\sl)\s+(-(literalpath|lp|path|PSPath)\s+)?(?<path>(?:\\").+?(?:\\")|"""[^"]+|''[^'']+|[^ ]+)') {
$Matches.path -replace '^(\\"|"""|'')' -replace '\\"$'
} else { # No Set-Location command passed, use the current dir.
$PWD.ProviderPath
}
The complexity of the solution comes from a number of factors:
Set-Location has multiple aliases.
The path may be passed positionally, with -Path or with -LiteralPath or its alias -PSPath.
Different quoting styles may be used (\"...\", """...""", '...'), or the path may be unquoted.
The command may still fail:
If the startup command uses prefix abbreviations of parameter names, such as -lit for -LiteralPath.
If a named parameter other than the path follows set-location (e.g., -PassThru).
If the string set-location is embedded in what PowerShell ultimately parses as a string literal rather than a command.
If the startup command is passed as a Base64-encoded string via -EncodedCommand.
[1] When you type powershell.exe into File Explorer's address bar instead, the currently open folder is made the working directory before PowerShell is started, and no startup command to change the working directory is passed; in that case, $PROFILE already sees the (ultimately) effective working directory.
1.open the registry (command:regedit)
2.find out the path \HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell\command (not \HKEY_CLASSES_ROOT\Directory\Background\shell\cmd\command)
3.the default value should be powershell.exe -noexit -command Set-Location -literalPath "%V"
4.you can change some param,
ps: you change the command to cmd.exe /s /k pushd "%V". if you do so, shift & right button in the explorer will open the cmd, not powershell

Are there any fundamental incompatibilities when using a CMD script in a console using PowerShell?

I have an extensive set of CMD scripts for an automation suite.
In a console using CMD.exe, everything works fine. After installing the Windows Creator's update, where PowerShell becomes the new default Windows shell via Explorer's menu options, my scripts break at-random. I can't provide any meaningful code for repro for two main reasons:
No obvious error is occurring; my automated scripts just hang, eventually
The halt isn't even occurring in the same place each time
What I can tell you is that the suite heavily relies on exit codes, use of findstr.exe, and type.
I know things like Windows macros, e.g., %Var% are not compatible, but I was assuming that since the only call I did was to a .bat file, .bat behavior would be the only thing I would need to worry about.
If that's not the case, should my initial .bat be triggering the direct execution of a CMD.exe instance with my parameters? If so, what's the best way to do that, PowerShell-friendly?
eryksun's comments on the question are all worth heeding.
This section of the answer provides a generic answer to the generic question in the question's title. See the next section for considerations specific to the OP's scenario.
Generally speaking, there are only a few things to watch out for when invoking a batch file from PowerShell:
Always include the specific filename extension (.bat or .cmd) in the filename, e.g., script_name.bat
This ensures that no other forms of the same command (named script_name, in the example) with higher precedence are accidentally executed, which could be:
In case of a command name without a path component:
An alias, function, cmdlet, or an external executable / PowerShell script (*.ps1) that happens to be located in a directory listed earlier in the $env:PATH (%PATH%) variable; if multiple executables by the same name are in the same (earliest) directory, the next point applies too.
In case of a command name with a path component:
A PowerShell script (*.ps1) or executable with the same filename root whose extension comes before .bat or .cmd in the %PATHEXT% environment variable.
If the batch file is located in the current directory, you must prefix its filename with ./
By design, as a security measure, PowerShell - unlike cmd.exe - does NOT invoke executables located in the current directory by filename only, so invoking script_name.bat to invoke a batch file of that name in the current directory does not work.[1]
Instead, you must use a path to target such an executable so as to explicitly signal the intent to execute something located in the current directory, and the simplest approach is to use prefix ./ (.\, if running on Windows only); e.g., ./script_name.bat.
When passing parameters to the batch file:
Either: be aware of PowerShell's parsing rules, which are applied before the arguments are passed to the batch file - see this answer of mine.
Or: use --% (the PSv3+ stop-parsing symbol) to pass the remaining arguments as if they'd been passed from a batch file (no interpretation by PowerShell other than expansion of %<var>%-style environment-variable references).
[1] eryksun points out that on Vista+ you can make cmd behave like PowerShell by defining environment variable NoDefaultCurrentDirectoryInExePath (its specific value doesn't matter).
While ill-advised, you can still force both environments to always find executables in the current directory by explicitly adding . to the %PATH% / $env:PATH variable; if you prepend ., you get the default cmd behavior.
As for your specific scenario:
After installing the Windows Creator's update, where PowerShell becomes the new default Windows shell via Explorer's menu options
This applies to the following scenarios:
Pressing Win-X (system-wide keyboard shortcut) now offers PowerShell rather than cmd in the shortcut menu that pops up.
Using File Explore's File menu now shows Open Windows PowerShell in place of Open command prompt (cmd).
However, nothing has changed with respect to how batch files are invoked when they are opened / double-clicked from File Explorer: The subkeys of HKEY_CLASSES_ROOT\batchfile and HKEY_CLASSES_ROOT\cmdfile in the registry still define the shell\open verb as "%1" %*, which should invoke a batch file implicitly with cmd /c, as before.
However, per your comments, your batch file is never run directly from File Explorer, because it require parameter values, and it is invoked in two fundamental ways:
Explicitly via a cmd console, after entering cmd in the Run dialog that is presented after pressing Win-R (system-wide keyboard shortcut).
In this case, everything should work as before: you're invoking your batch file from cmd.
Explicitly via PowerShell, using File Explorer's File menu.
Per your comments, the PowerShell console may either be opened:
directly in the directory in which the target batch file resides.
in an ancestral directory, such as the root of a thumb drive on which the batch file resides.
In both cases, PowerShell's potential interpretation of arguments does come into play.
Additionally, in the 2nd case (ancestral directory), the invocation will only work the same if the batch file either does not depend on the current directory or explicitly sets the current directory (such as setting it to its own location with cd /d "%~dp0").
This is a non-answer solution if encountering the question's specific behavior. I've verified all my halting scripts stopped halting after implementing a shim-like workaround.
As erykson said, there doesn't appear to be a reason why using a shim would be required. The goal is then to explicitly launch the script in CMD when using PowerShell, which aligns with Jeff Zeitlin's original suggestion in the question's comments.
So, let's say you're in my shoes with your own script_name.bat.
script_name.bat was your old script that initializes and kicks off everything. We can make sure that whatever was in script_name.bat is correctly run via CMD instead of PowerShell by doing the following:
Rename script_name.bat to script_name_shim.bat
Create a new script_name.bat in the same directory
Set its contents to:
#echo off
CMD.exe /C "%~dp0script_name_shim.bat" %*
exit /b %errorlevel%
That will launch your script with CMD.exe regardless of the fact that you started in PowerShell, and it will also use all your command-line arguments too.
This looks like a chicken egg problem, wihtout knowing the code it's difficult to tell where the problem is.
There are a ton of ways to start batches with cmd.exe even in win10cu.
Aliases are only a problem when working interactively with the PowerShell console and expecting behavior as it used to be in cmd.exe.
The aliases depend also on the loaded/imported modules and profiles.
This small PowerShell script will get all items from Help.exe and
perform a Get-Command with the item.
internal commands without counterparts in PoSh are filtered out by the ErrorAction SilentlyContinue.
Applications (*.exe files) are assumed identical and removed by the where clause.
help.exe |
Select-String '^[A-Z][^ ]+'|
ForEach-Object {
Get-Command $_.Matches.Value -ErrorAction SilentlyContinue
}| Where-Object CommandType -ne 'Application'|Select *|
Format-Table -auto CommandType,Name,DisplayName,ResolvedCommand,Module
Sample output on my system, all these items will likely work differently in PowerShell:
CommandType Name DisplayName ResolvedCommand Module
----------- ---- ----------- --------------- ------
Alias call call -> Invoke-Method Invoke-Method pscx
Alias cd cd -> Set-LocationEx Set-LocationEx Pscx.CD
Alias chdir chdir -> Set-Location Set-Location
Alias cls cls -> Clear-Host Clear-Host
Alias copy copy -> Copy-Item Copy-Item
Alias del del -> Remove-Item Remove-Item
Alias dir dir -> Get-ChildItem Get-ChildItem
Alias echo echo -> Write-Output Write-Output
Alias erase erase -> Remove-Item Remove-Item
Alias fc fc -> Format-Custom Format-Custom
Function help pscx
Alias md md -> mkdir mkdir
Function mkdir
Function more
Alias move move -> Move-Item Move-Item
Function Pause
Alias popd popd -> Pop-Location Pop-Location
Function prompt
Alias pushd pushd -> Push-Location Push-Location
Alias rd rd -> Remove-Item Remove-Item
Alias ren ren -> Rename-Item Rename-Item
Alias rmdir rmdir -> Remove-Item Remove-Item
Alias set set -> Set-Variable Set-Variable
Alias sc sc -> Set-Content Set-Content
Alias sort sort -> Sort-Object Sort-Object
Alias start start -> Start-Process Start-Process
Alias type type -> Get-Content Get-Content

How to "open with" in a batch file

I have a windows powershell script that will be available on a server to my users. I don't want them to have to go out and find the PS script, right click and click "run with powershell" or do an "open with". The Windows (Win 7 at least) default program is notepad.
I want to make a batch file to do this. I've tried:
start "c:\myfile.ps1" powershell.exe
and a few other variations, but all I've been able to do is either start powershell, or open my file in its default program, notepad.
Any advice is appreciated! Thanks!
Bonus question: If I run my batch file as administrator will it also run my PS script as administrator?
Simply use the -file argument for PowerShell.exe in your batch file:
PowerShell.exe -file c:\MyFile.ps1
Additionally, some users may have their Execution Policy set to something that would restrict scripts from being executed, so you may want to do something like:
PowerShell.exe -ExecutionPolicy Bypass -file c:\MyFile.ps1
If you would like to use start to launch it you can do so as Ansgar Wiechers noted by running:
start "" PowerShell.exe -ExecutionPolicy Bypass -file c:\MyFile.ps1
Some notes regarding using start: By default it will launch PowerShell in a separate window, and continue to execute the rest of the batch file without waiting for the PowerShell window to close. If that is undesirable you have two options. You can specify /wait which will wait for the PowerShell window to close before continuing the batch file, or you can use the /B option will will not open a new window, and will execute PowerShell in the current console window.
And finally, yes if your batch file is run under the Administrator context, PowerShell will be as well.

powershell command not running in .bat file?

I am trying to make a simple script to set my gcc variables. as a .bat file.
the variable is set like this
$env:Path += ";C:\Users\Brett\Compilers\MinGW\bin"
this runs just fine when I type/paste it into power shell.
but when I paste into a script myscript.bat, and run it through powershell I get this error:
C:\Users\Brett\Compilers>"$env:Path += ";C:\Users\Brett\Compilers\MinGW\bin""
The filename, directory name, or volume label syntax is incorrect.
PS C:\Users\Scruffy\Compilers>
PowerShell is a seperate execution enviroment from with Windows Command Line (cmd.exe)
If you want to run powershell commands from a batch file you need to save the powershell script (.ps1) and pass it into powershell.exe as a command line argument.
Example:
powershell.exe -noexit c:\scripts\test.ps1
More Information is available here on Microsoft TechNet
In general, leave batch stuff to .BAT files and put PowerShell stuff into .ps1 files.
I can duplicate your results here - but those are to be expected. Cmd.exe sees a string then a path and then gets quite confused as the syntax is not one that the command prompt can handle. So it gives that error message.
If you want to add stuff to your path, then why not put the statement inside a .ps1 script file?
As mentioned by others, you need to save the code in a .ps1 file and not .bat.
This line (from Setting Windows PowerShell path variable) will do the trick:
$env:Path = $env:Path + ";C:\Users\Brett\Compilers\MinGW\bin"
Or even shorter:
$env:Path += ";C:\Users\Brett\Compilers\MinGW\bin"

How to run a PowerShell script

How do I run a PowerShell script?
I have a script named myscript.ps1
I have all the necessary frameworks installed
I set that execution policy thing
I have followed the instructions on this MSDN help page
and am trying to run it like so:
powershell.exe 'C:\my_path\yada_yada\run_import_script.ps1' (with or without --noexit)
which returns exactly nothing, except that the file name is output.
No error, no message, nothing. Oh, when I add -noexit, the same thing happens, but I remain within PowerShell and have to exit manually.
The .ps1 file is supposed to run a program and return the error level dependent on that program's output. But I'm quite sure I'm not even getting there yet.
What am I doing wrong?
Prerequisites:
You need to be able to run PowerShell as an administrator
You need to set your PowerShell execution policy to a permissive value or be able to bypass it
Steps:
Launch Windows PowerShell as an Administrator, and wait for the PS> prompt to appear
Navigate within PowerShell to the directory where the script lives:
PS> cd C:\my_path\yada_yada\ (enter)
Execute the script:
PS> .\run_import_script.ps1 (enter)
Or: you can run the PowerShell script from the Command Prompt (cmd.exe) like this:
powershell -noexit "& ""C:\my_path\yada_yada\run_import_script.ps1""" (enter)
according to Invoking a PowerShell script from cmd.exe (or Start | Run) by Kirk Munro.
Or you could even run your PowerShell script asynchronously from your C# application.
If you are on PowerShell 2.0, use PowerShell.exe's -File parameter to invoke a script from another environment, like cmd.exe. For example:
Powershell.exe -File C:\my_path\yada_yada\run_import_script.ps1
If you want to run a script without modifying the default script execution policy, you can use the bypass switch when launching Windows PowerShell.
powershell [-noexit] -executionpolicy bypass -File <Filename>
Type:
powershell -executionpolicy bypass -File .\Test.ps1
NOTE: Here Test.ps1 is the PowerShell script.
I've had the same problem, and I tried and tried... Finally I used:
powershell.exe -noexit "& 'c:\Data\ScheduledScripts\ShutdownVM.ps1'"
And put this line in a batch-file, and this works.
If you only have PowerShell 1.0, this seems to do the trick well enough:
powershell -command - < c:\mypath\myscript.ps1
It pipes the script file to the PowerShell command line.
Pretty easy. Right click the .ps1 file in Windows and in the shell menu click on Run with PowerShell.
Open PowerShell in administrator mode
Run: set-executionpolicy unrestricted
Open a regular PowerShell window and run your script.
I found this solution following the link that was given as part of the error message: About Execution Policies
Make sure to run set-ExecutionPolicy default once you're done, or you will be exposed to security risks.
Using cmd (BAT) file:
#echo off
color 1F
echo.
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "PrepareEnvironment.ps1"
:EOF
echo Waiting seconds
timeout /t 10 /nobreak > NUL
If you need run as administrator:
Make a shortcut pointed to the command prompt (I named it
Administrative Command Prompt)
Open the shortcut's properties and go to the Compatibility tab
Under the Privilege Level section, make sure the checkbox next to "Run this program as an administrator" is checked
An easy way is to use PowerShell ISE, open script, run and invoke your script, function...
In case you want to run a PowerShell script with Windows Task Scheduler, please follow the steps below:
Create a task
Set Program/Script to Powershell.exe
Set Arguments to -File "C:\xxx.ps1"
It's from another answer, How do I execute a PowerShell script automatically using Windows task scheduler?.
If your script is named with the .ps1 extension and you're in a PowerShell window, you just run ./myscript.ps1 (assuming the file is in your working directory).
This is true for me anyway on Windows 10 with PowerShell version 5.1 anyway, and I don't think I've done anything to make it possible.
Give the path of the script, that is, path setting by cmd:
$> . c:\program file\prog.ps1
Run the entry point function of PowerShell:
For example, $> add or entry_func or main
You can run from cmd like this:
type "script_path" | powershell.exe -c -
Use the -File parameter in front of the filename. The quotes make PowerShell think it is a string of commands.
I've just found the method what Microsoft do when we right click on a ps1 script and click on "Run with PowerShell" :
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-Command" "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'C:\Users\USERNAME\Desktop\MYSCRIPT.ps1'"
With the appropriate execution policy, you should just be able to call the file directly and Windows will associate it with PowerShell
C:\my_path\yada_yada\run_import_script.ps1
That does not do so well with arguments. The real answer to your question is that you are missing the & to say "execute this"
powershell.exe '& C:\my_path\yada_yada\run_import_script.ps1'

Resources