Nothing solves everything – PowerShell and other technologies

Raymond recently wrote a post titled, “A new scripting language doesn’t solve everything.”  In that post (and in a previous one,) he points out that backwards compatibility is extremely important for cmd.exe (with itself,) and for PowerShell (with existing cmd.exe scripts.)  More importantly, the point is that PowerShell doesn’t solve everything:



Shipping a new command shell doesn’t solve everything either. For one thing, you have to decide if you are going to support classic batch files or not. Maybe you decide that you won’t and prefer to force people to rewrite all their batch files into your new language. Good luck on that.


On the other hand, if you decide that you will support batch files after all, then presumably your new command shell will not execute old batch files natively, but rather will defer to CMD.EXE. And there’s your problem: You see, batch files have the ability to modify environment variables and have the changes persist beyond the end of the batch file.



Similar threads have come up in the newsgroups in relation to VBScript.


Raymond is absolutely correct, and you won’t find a single person on the PowerShell team that says otherwise.  Or has ever said otherwise, for that matter.  PowerShell is not a swap-in replacement for cmd.exe, it is not a swap-in replacement for the Windows Scripting Host, does not need to re-implement every command-line tool that ships in Windows, and does not solve world hunger.  Tensions that suggest differently are misguided.


Many people – and many systems – depend on the subtle details of the tool with which they’ve chosen to solve their problem.  If you want to write an application that understands the source files for all related technologies (ie: cmd.exe scripts, or .vbs files,) then you’d better make sure that you re-implement every nuance and bug as well.  That includes the bugs that even the owners of those related technologies don’t know about, because I guarantee that a script somewhere depends on it.


The solution isn’t to burn your time by rewriting your systems, either.  If you have code that works – keep it!  I currently have files from 41 different technologies in my “tools” directory:



[D:\lee\tools]
PS:170 > dir | group Extension | sort -desc Count


Count Name                      Group
—– —-                      —–
  275 .exe                      {adlb.exe, ansi2knr.exe, atmar
   59 .ps1                      {#share-file.ps1, backup-chang
   39 .bat                      {articlecounter.bat, c.bat, ch
   25 .dll                      {acctinfo.dll, AutoItX3.dll, B
   20 .cmd                      {d.cmd, devdiv.cmd, dumpfsmos.
   16 .vbs                      {checkrepl.vbs, clean.vbs, clo
    8 .js                       {calc.js, cio.js, d2.js, evt2c
    7                           {%backup%~, 0, _viminfo, autom
    6 .config                   {cordbg.exe.config, perfcompar
    5 .hlp                      {depends.hlp, srvmgr.hlp, usrm
    5 .ini                      {KeePass.ini, memtriage.ini, r
    5 .txt                      {cmgetcer.txt, getcm.txt, inst
    4 .doc                      {kernrate.doc, mqcast.doc, prn
    4 .inf                      {clusfileport.inf, clusfilepor
    3 .reg                      {intfiltr.reg, samplereasons.r
    3 .chm                      {clusterrecovery.chm, eventcom
<snip>


Rewriting for the sake of rewriting provides a poor return on investment – interop is the key word here.  If you want to extend your existing systems, you can do that in the tool of your choice.  Most systems can be extended to include other technologies – a typical Windows build, for example, takes advantage of dozens of different tools.  If you want to write new scripts or systems, you can also do that in the tool of your choice. 


In both cases, PowerShell happens to be a tool that solves certain problems extremely efficiently.


To address one specific detail in Raymond’s post,



“You see, batch files have the ability to modify environment variables and have the changes persist beyond the end of the batch file.”


Try this script.  From a PowerShell.exe window, this wrapper script allows you to run batch files that persistently modify their environment variables.




############################################################################## 
## 
## Invoke-CmdScript.ps1 
## 
## Invoke the specified batch file (and parameters), but also propigate any 
## environment variable changes back to the PowerShell environment that 
## called it. 
## 
## ie: 
## 
## PS > type foo-that-sets-the-FOO-env-variable.cmd 
## @set FOO=%* 
## echo FOO set to %FOO%. 
##  
## PS > $env:FOO 
##  
## PS > Invoke-CmdScript “foo-that-sets-the-FOO-env-variable.cmd” Test
##  
## C:\Temp>echo FOO set to Test. 
## FOO set to Test. 
##  
## PS > $env:FOO 
## Test 
## 
############################################################################## 

param([string] $script, [string] $parameters) 

$tempFile = [IO.Path]::GetTempFileName() 

## Store the output of cmd.exe.  We also ask cmd.exe to output  
## the environment table after the batch file completes 

cmd /c ” `”$script`” $parameters && set > `”$tempFile`” “

## Go through the environment variables in the temp file. 
## For each of them, set the variable in our local environment. 
Get-Content $tempFile | Foreach-Object {  
    if($_ -match “^(.*?)=(.*)$”
    {
        Set-Content “env:\$($matches[1])” $matches[2
    }


Remove-Item $tempFile


[Edit: Updated to make more usable for commands with spaces in their names.]

12 Responses to “Nothing solves everything – PowerShell and other technologies”

  1. Jeffrey Snover writes:

    PSMDTAG:PHILOSOPHY: PowerShell is not a drop-in replacement for CMD.EXE

    PSMDTAG:PHILOSOPHY: PowerShell does not solve world hunger.

    PSMDTAG:PHILOSOPHY: When to rewrite an existing function in PowerShell.

    PSMDTAG:FAQ: How can I pick up changes to environment variables after I run a .BAT file?

    PSMDTAG:DOTNET: [IO.PATH]::GetTypeFileName()

    Jeffrey Snover [MSFT]
    Windows PowerShell/Aspen Architect
    Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
    Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

  2. Joshua Flanagan writes:

    This script doesn’t seem to handle long filename parameters – or batch scripts that call other batch scripts.

    I wanted to use it to turn my PowerShell into a Visual Studio 2005 Command Prompt (a common task for developers, I would assume).
    I many permutations of the following command, with no luck:
    . invoke-cmdscript "C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat" x86

    Finally, I had to settle for adding the following to my $profile:
    pushd "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\"
    .$home\invoke-cmdscript.ps1 vsvars32.bat
    popd

    Is there a more elegant solution? Or am I trying to do something I shouldn’t?

  3. Lee writes:

    Actually, it all needs to be in quotes. The comment header shows an example of a script that takes arguments.

  4. Marcus Widerberg writes:

    Yeah, it’s great to have the "opportunity" to juggle 7 text editors, 4 shells, 5 MUAs, … everyone loves that part of computing.

    But you’re right, if you can’t solve world hunger, why try to make powershell a complete replacement to cmd.exe. I mean, that must be so hard, cmd.exe being generally recognized as such a full fledged shell with heaps of possibilities…

    </sarcasm and stuff>

    Seriously, what is the best way of getting vs.net env vars into powershell?

    *and I return to my google journey*

  5. Lee writes:

    Hi Marcus;

    There are three approaches:

    1) Make a PowerShell equivalent of vcvarsall.bat
    2) Use the script given above to run vcvarsall.bat
    3) Start vcvarsall.bat, and then type ‘powershell’ to start a PowerShell prompt from within.

    Lee

  6. yoshi writes:

    I just made this from C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat, and works for me.

    $env:VSINSTALLDIR = "C:\Program Files\Microsoft Visual Studio 8"
    $env:VCINSTALLDIR = "C:\Program Files\Microsoft Visual Studio 8\VC"
    $env:FrameworkDir = "C:\WINDOWS\Microsoft.NET\Framework"
    $env:FrameworkVersion = "v2.0.50727"
    $env:FrameworkSDKDir = "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0"

    write-output "Setting environment for using Microsoft Visual Studio 2005 x86 tools."

    $env:DevEnvDir = "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE"

    $env:PATH = "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE;C:\Program Files\Microsoft Visual Studio 8\VC\BIN;C:\Program Files\Microsoft Visual Studio 8\Common7\Tools;C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\bin;C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\bin;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\bin;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 8\VC\VCPackages;" + $env:path
    $env:INCLUDE = "C:\Program Files\Microsoft Visual Studio 8\VC\ATLMFC\INCLUDE;C:\Program Files\Microsoft Visual Studio 8\VC\INCLUDE;C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\include;" + $env:INCLUDE
    $env:LIB = "C:\Program Files\Microsoft Visual Studio 8\VC\ATLMFC\LIB;C:\Program Files\Microsoft Visual Studio 8\VC\LIB;C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\lib;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\lib;" + $env:LIB
    $env:LIBPATH = "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 8\VC\ATLMFC\LIB"

  7. Nick writes:

    Thanks! This is going in my toolbox.

  8. ChernoJ writes:

    Does this address the situation where the spawned script unsets an environment variable, and you want that change to propagate back to the powershell script so the variable no longer exists when the spawned script is done?

  9. drozzy writes:

    Thanks for the script, but there are problems with your quotes. When copying and pasting they are all incorrect.

    Would be great if this was available as a script-file download, not a copy-paste.

    Cheers

  10. Lee Holmes writes:

    @Drozzy – Your wish is my command :)

    It is part of the PowerShell Cookbook. You can download the samples free from here: http://examples.oreilly.com/9780596801519/, and also PoshCode: http://poshcode.org/2176.

    Lee

  11. Jason writes:

    Lee, can you fix the quotes in your post so we can copy and paste rather than have to download and unzip the linked files in your last comment? One of the URLs (poshcode) from your last comment already is not working (it times out). How long until the other one breaks too?

  12. ticapix writes:

    Nice :)

    Same feature but without the temporary file

    function ExportEnv-Cmd ($script, $parameters) {
    & $env:comspec /c ” `”$script`” $parameters && set” | Foreach-Object {
    if($_ -match “^(.*?)=(.*)$”)
    {
    Set-Content “env:\$($matches[1])” $matches[2]
    }
    }
    }

Leave a Reply