3

I'm trying to implement, in a Windows batch file, the logic "if %MyBinaryDir% is not already at the beginning of the system %PATH%, then put it there; if it's already there, do nothing".

I've got this:

@echo %PATH% | findstr /i /b /c:"%MyBinaryDir%;" > nul || set "PATH=%MyBinaryDir%;%PATH%

This has always worked pretty well, until I tried to deploy on someone's machine where for some unearthly reason the %PATH% variable contained an odd number of quote characters.

The problem boils down to this:

@set x="
@echo %x%
@echo %x% | more

The second line prints a single " character. The third line is tripped up by the quote character and fails to pipe the echo output to the second binary (in my case findstr, but in this boiled-down example more) at all. Instead, it literally prints the characters:

" | more

So my questions are:

(Y) How do I safely pipe any arbitrary string into a second binary?

and/or

(X) Is there a way of conditionally-prepending directories to the system path that avoids this mess?

peak
  • 105,803
  • 17
  • 152
  • 177
jez
  • 14,867
  • 5
  • 37
  • 64
  • I guess one solution is to write a separate batch file called `echopath.bat` containing the single line `@echo %PATH%`, and then invoke it as `@call echopath | findstr ...` But I'd like to keep the solution all within one file if possible. – jez Jan 08 '16 at 05:30
  • Does your application **require** the directory to be prepended? Could it simply be prepended if it isn't already in the path? – Magoo Jan 08 '16 at 05:32
  • @Magoo "Could it simply be prepended if it isn't already in the path?" Ah, after initial confusion, I get it. You mean "if it's *anywhere* in the path, not necessarily at the beginning". No, it needs to be at the beginning. The `%PATH%` might, and frequently does, contain multiple `C:\PythonXX` directories, and I want to ensure that my chosen one is promoted to the front and therefore guaranteed to be accessed when I type `python.exe` – jez Jan 08 '16 at 05:33

2 Answers2

3

Use delayed expansion to echo any variable content in a safe way.

setlocal EnableDelayedExpansion
set x="
echo !x!
echo !x! | more
echo !path! | more

And to be safe also with empty variables you could use echo(

echo(!path! | more

There is also a question about pretty print/split the path variable

Community
  • 1
  • 1
jeb
  • 78,592
  • 17
  • 171
  • 225
  • Thanks, I guess I can work with this. Ideally I wanted to remove my target directory or directories from the `%PATH%` if they're already there and *move* them to the front, but as your answer http://stackoverflow.com/a/5472168/3019689 to the splitting question indicates, that gets really insane/unmaintainable in these pathological cases where there are `"` and/or `;` in the `%PATH%`. Having read that answer, I'm now unsure whether the problem computer had directories with `"` in the name, or directories with `;` in the name that were then quoted to avoid interpretation as a path separator. – jez Jan 08 '16 at 17:27
0

I suggest to clean up PATH as no directory path in this environment variable should contain ever quotes.

Here is an example batch code for your task:

@echo off
rem Uncomment the line below for testing with C:\PythonXX
rem already in PATH with an erroneous double quote in PATH.
rem set "PATH=C:\PythonXX;"%PATH%"

echo Path before clean up:
echo.
echo PATH=%PATH%
set "PATH=%PATH:"=%"
echo.
echo Path after clean up:
echo.
echo PATH=%PATH%
if /I "%PATH:~0,12%" NEQ "C:\PythonXX;" set "PATH=C:\PythonXX;%PATH%"
echo.
echo Path modified:
echo.
echo PATH=%PATH%
echo.
pause

See the answer on set environment variables with spaces and the other answers referenced there why set "variable=value" should be used even if the value to assign to a variable contains 1 or more quotes itself.

Community
  • 1
  • 1
Mofi
  • 46,139
  • 17
  • 80
  • 143
  • See also [How to search and replace a string in environment variable PATH?](http://stackoverflow.com/a/24650324/3074564) It is about how to replace an existing Python path by another Python path in system __PATH__ which could be extended for your task requirements. – Mofi Jan 08 '16 at 07:29
  • Sometimes it's necessary that the path contains quotes. Ex. `"C:\Music ; Audio"` contains a semicolon in the directory name. So the PATH contains `PATH=C:\windows\system32;"C:\Music ; Audio"` – jeb Jan 08 '16 at 09:52
  • 1
    Yes, that's right. This exception exists. Directory paths with a semicolon inside should be simply not allowed in my opinion as there is really never a reason to have a semicolon in a directory name at all. However, an odd number of quotes in __PATH__ requires that either a human makes this correction or a script tries to find out where missing quote should be inserted in __PATH__ by verifying which directories in __PATH__ really exist. Removing one of the quotes in `var` example in [your pretty print/split the path answer](http://stackoverflow.com/a/5472168/3074564) demonstrates that issue. – Mofi Jan 08 '16 at 11:27
  • Hmm, I agree that it *shouldnt* be there, in any sane world, but I'd like to work with the fact that it unfortunately *is*. Who knows, maybe some vital binary is in one if the pathologically-named dirs? I had thought about the substring method—unfortunately the dir I'm promoting to the start of the path might not always have exactly 11 chars (`c:\PythonXX` was just an example) so the hard coded 12 is a problem. I looked for methods of finding the length of a string in batch and they're pretty horrible—the whole solution just seemed to be getting hairier and hairier. – jez Jan 08 '16 at 13:30
  • 1
    @jez Take a look on batch function [strLen](http://www.dostips.com/DtCodeCmdLib.php#strLen) if you need to get the length of a string of a variable. – Mofi Jan 08 '16 at 15:43