In windows, I have two .bat files, say dir_a/a.bat, and dir_b/b.bat.
What I want is that after executing a.bat, I will be able to call b.bat. My approach now is to set a PATH to dir_b, so in a terminal that executed a.bat, I can just call b.bat and will be able to execute b.bat. Yet putting "set PATH=dir_b;%PATH%" in a.bat is not working. What did I do wrong?
For the case that you're dealing with a relative path:
You might notice that:
set path=%path%;"\..\..\..\vc98\bin\"
will ^^ NOT work ^^ !
So do it like this:
pushd "..\..\..\vc98\bin\"
path %cd%; %path%
popd
...and of course a
set path=%path%;%cd%
between the pushd and popd will also do the trick
Well for also have a look here:
https://stackoverflow.com/a/6595206/3135511
...
call :setAbsPath ABS_PATH ..\
...
^-To see to do it via the self made subfunction 'setAbsPath'
-> or instead of call you may also use For - details in the other thread
And just a small side note for those who might also like to run Microsoft Visual C++ 6.0(anno 1998) > without install it...
... and wonder where's that f*** 'standard' include ?!
There are about 17 file in \vc98\include\ that have been manually chopped 8 + 3 chars. Like:
algrithm -> algorithm
strstrem -> strstream
xception -> exception
So be aware and creative about that !
You must include the absolute path to b.bat file; for example:
set PATH=C:\User A\Folder X\dir_b;%PATH%
I suspect that you have a SETLOCAL in a.bat. ANY environment changes made after a SETLOCAL are backed out when the matching ENDLOCAL (or EOF in the same context) is reached.
Depending on how you are terminating a.bat, you'd need something in the order of ENDLOCAL&set "Path=dir_b;%PATH%"&GOTO :EOF which will prepend dir_b to your existing path as you appear to exepect for the duration of that particular CMD session.
Don't use PATH because that conflicts with the Windows Path. Instead you could add the following:
pushd path_to_your_dir_b
Then add popd in an appropriate place
Related
What paths are searched by start? Is this path list queryable via WMI, the registry, WSH shell folder constants, .NET methods, or any other Windows API?
In a cmd console, the start command is sort of magic. Say your default web browser is Firefox. You also have Chrome and Opera installed, but they are neither set as default, nor associated with any file types, nor in your %PATH%. Considering the following example:
start "" opera https://stackoverflow.com
... why does that work?
The reason this matters is that, for all the start command's strengths, it is still perilous to retrieve an application's PID or window handle when that application was launched with start. In a cmd environment I'd prefer to use wmic process call create in a for /F loop to capture the PID. So is there a way I can teach wmic process call create to launch "opera" or "iexplore" or "outlook" or "winword" or other applications as start can without fully qualifying the drive:\\path\\to\\executable?
I thought I was on to a solution by scanning HKLM\Software\Clients recursively for shell\open\command, then adding each referenced location temporarily to %PATH%:
rem // temp add installed clients to %PATH% to make "call :display" behave like "start"
setlocal enabledelayedexpansion
for %%a in (HKLM HKCU) do for /f "tokens=2*" %%I in (
'REG QUERY %%a\Software\Clients /s /f command /k /ve ^| find "(Default)"'
) do if "%%~I"=="REG_EXPAND_SZ" (
if /i "%%~xJ"==".exe" (
if "!PATH:%%~J\..=!"=="!PATH!" path !PATH!;%%~J\..
) else for %%# in (%%J) do if /i "%%~x#"==".exe" (
if "!PATH:%%~#\..=!"=="!PATH!" path !PATH!;%%~#\..
)
) else (
if /i "%%~xJ"==".exe" (
if "!PATH:%%~dpJ=!"=="!PATH!" path !PATH!;%%~dpJ
) else (
for %%# in (%%J) do if /i "%%~x#"==".exe" (
if "!PATH:%%~dp#=!"=="!PATH!" path !PATH!;%%~dp#
)
)
)
endlocal & path %PATH%
That works reasonably well, but it still falls short of what start can access. For example:
start "" wordpad
works, whereas
wmic process call create "wordpad.exe"
fails.
I remember a discussion about this very topic either on DosTips.com or here on SO but I cannot find it. Hopefully somebody does so we can mark this question as a duplicate.
We know that the batch files will search the current directory and then the path variable to find an executable. The START command also searches the following Registry path
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
So you have a few options to find the executable.
You can use the WHERE command which searches the current directory and the directories in the PATH variable.
where notepad.exe
You can also just search the path variable with a FOR command.
for %%I in (notepad.exe) do echo %%~$PATH:I
And of course you can search the registry path as I mentioned above by doing a REG QUERY.
Edit2
This may be of interest regards to what you are doing (as opposed to why something works the way it does).
From a previous answer
Can I create shorthand names for use inside cmd?
See Doskey /?.
You could do this doskey cddesk=cd "%userprofile%\desktop"
Then type cddesk.
By putting all your macros in a batch file you can autoload. Of course
you could do this with batchfiles too.
From Technical Reference to the Windows 2000 Registry, Microsoft,
2000.
AutoRun HKCU\Software\Microsoft\Command Processor
Data type Range Default value
REG_SZ list of commands There is no default value for this entry.
Description
Contains commands which are executed each time you start Cmd.exe.
Also see this batchfile https://pastebin.com/A2qLw8r3 that list
special names.
In a command prompt start shell:desktop
Or in Start - Run (Winkey + R) just shell:desktop
Edit
I'll just add some caveats here.
This is about opening executables using Start command. A massive amount of rules apply to non executable files.
Not using Start CMD pre-processes the command line and sends a FQFN to CreateProcess - so it's CMD that searches the path not CreateProcess.
https://learn.microsoft.com/en-us/windows/desktop/shell/launch is the documentation for ShellExecute. Everything ends up calling CreateProcess https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessw. Plus CMD preprocessing is detailed here https://books.google.com.au/books/about/Windows_NT_Shell_Scripting.html?id=jrdQAAAAMAAJ&redir_esc=y. Also start /? provides documentation for all three ways to start files. A summary is here Command to run a .bat file
Some fun facts. CMD if it can't recognise a file extension will attempt to execute it. ShellExecute will content sniff unknown files https://learn.microsoft.com/en-us/windows/desktop/com/filetype-key.
quote from rojo: Thanks for the link dump. I'm struggling to understand how ShellExecute can save me from having to fully qualify path names though. That was my primary question. "To use ShellExecute or ShellExecuteEx, your application must specify the file or folder object that is to be acted on, and a verb that specifies the operation." It's the "specify the file or folder object" part that I'm asking about.
It uses the 6 steps under CreateProcess and if that turns up nothing it uses App Paths. If a verb is not specified the default verb is used (usually Open). See my answer here How do i automate the simulation of right clicking a file and selecting the first option using vbscript.
So it an intersection of 4 technologies. Compatibility with MSDos, changes made by IBM for OS/2 and updated by Microsoft for Windows 2000, the standard windows way of starting programs CreateProcess, and with Windows 95 we got shell functions which are based on OLE
quote from rojo: Snap! You're right! In PowerShell I was able to create a System.Diagnostics.Process object, set $ps.StartInfo.UseShellExecute = $true, and then start "wordpad" without any file extension or fully qualified path. Setting the .UseShellExecute property to $false caused the execution to fail. How interesting!
I have a batch file that copies and moves stuff, but I am getting stuck dealing with Certificates. I have to use a command the vendor provides called installrootcacert.cmd, but I also need to pass the file name of the cert which is aptly named rootca.cer. I have to use the script the vendor provides so there is no way around that.
Normally I would run this from the command like like so:
c:\vendor\Software\Conf\Security\installrootcacert.cmd rootca.cer
I have attempted to call the command from my batch file, but with no luck.
I tried to use a variable, but because that command calls several other processes, it is looking for "rootca.cer" after the command. If I place it in a variable, the other processes fail. I cannot modify the other processes.
echo #off
cd E:\vendor\Software\Conf\Security\trustedCA
e:
call "e:\vendor\Software\Conf\Security\installrootcacert.cmd rootCA.cer"
A possible solution, might be:
#echo off
cd /d "E:\vendor\Software\Conf\Security\trustedCA"
call "E:\vendor\Software\Conf\Security\installrootcacert.cmd" rootCA.cer
echo #off replaced with #echo off because echo #off will echo #off in cmd.
It is mentioned by Compo in comments, you are changing drives: use /d option with cd.
Finally, quote only the filename in the call command, else, windows will interpret this as a complete filename (i.e. E:\vendor\Software\Conf\Security\installrootcacert.cmd rootCA.cer, so filename will be installrootcacert.cmd rootCA.cer).
Try the following. You need to adjust the # in your echo statement and your quotations:
#echo off
cd E:\vendor\Software\Conf\Security\trustedCA
e:
call "e:\vendor\Software\Conf\Security\installrootcacert.cmd" rootCA.cer
The reason that this works is because putting the command #echo off in your script stops ALL commands from being output. If you just have echo #off, you're literally going to echo that. (credit to double-beep for initially suggesting that)
As for the quotes, what you are trying to do is pass that as a command, so when you call the rootCA.cer, you need to make sure you are passing it the proper parameters, which is why you place that filepath in quotes. If you place the WHOLE object in quotes, you aren't actually issuing a call to the rootCA.cer command. (credit to LotPings for initially suggesting that).
I'm running a batch file which updates some variables, notably %PATH%. My environment has a known bug where on of the directories in %PATH% is quoted, i.e.
PATH=c:\windows;...;"c:\program files\foo"\bin;c:\program files\...
I have a script which appends to PATH. When I do it inside an IF block, I get an error, e.g:
IF "1"=="1" (
SET "PATH=%PATH%;c:\foo"
)
Gives the error
\Microsoft was unexpected at this time.
Where \Microsoft is obviously a fragment from one of the directories in %PATH%.
I don't get the error if the SET is not within a conditional block. Why is this?
Edit: It seems that this has more to do with the fact that PATH also contains parenthesis. Here's a better example of the issue:
C:\temp>SET "FOO=C:\Program Files (x86)\;foo"
C:\temp>ECHO %FOO%
C:\Program Files (x86)\;foo
C:\temp>IF "1"=="1" ( ECHO %FOO% )
\ was unexpected at this time.
C:\temp>IF "1"=="1" ECHO %FOO%
C:\Program Files (x86)\;foo
So my question is really, why does it break if it's in the paren-delimited block?
JosefZ properly identified the problem with ) in the path, but his suggested solution of removing all quotes is not safe.
Any path within the PATH variable may include ;, !, &, ) and/or ^, all of which can cause various issues when using normal %PATH% expansion.
There may be some paths with & or ) etc. quoted, as well as some unquoted paths with problem characters, so both %PATH% and "%PATH%" can fail.
The only guaranteed safe way to expand PATH is via delayed expansion, but you want the new value to survive the ENDLOCAL. What to do? . . .
. . . FOR /F and delayed expansion toggling to the rescue :-)
if "1"=="1" (
setlocal enableDelayedExpansion
for /f "eol=: delims=" %%P in ("!path!") do (
endlocal
set "path=%%P;c:\foo"
)
)
Simplistic implementation of PATH extension via code like set path=%path%;c:\foo,
or set "path=%path%;c:\foo" is rampant, but it is not safe. By and large, people do not realize the subtle complexities involved with PATH management.
If you are ever trying to modify the PATH variable in a batch script that can be released into the wild, then you should always use a safe method like the one I have shown above.
The problem becomes even more complex if you want to conditionally append a path to PATH if and only if path is not already there. See How to check if directory exists in %PATH%? for a list of potential issues, as well as a fairly robust solution.
SET "PATH=%PATH:"=%;c:\foo"
should fix the problem, since the quoes are irrelevant
Caution: This may not be true if a path-entry contains ";". In that case, you should rename the directory to something sensible.
You are right that this has more to do with the fact that PATH also contains parentheses. Next example shows the issue unambiguously:
==> ( set "varName=var"value(s) with"space(s) and"right parentheses"" )
with"space(s) and"right was unexpected at this time.
==> ( set varName=var"value(s) with"space(s) and"right parentheses" )
and"right parentheses" was unexpected at this time.
==>
Read answers to How does the Windows Command Interpreter (CMD.EXE) parse scripts? for explanation.
I can see the only solution: correct (adjust) your path variable once for always by removing all " double quotes as follows (check user variable path as well if present). Restart required.
Just remove the outer quotes in your setting of the path variable. They're not required, by any means. Certainly not in this instance, where you have quotes being used internally within those quotes. The quotes should be inside your path variable, not outside the whole thing.
SET PATH=%PATH%;c:\foo
To further how it should be done, here's some additional examples to clarify...
SET PATH=%PATH%;C:\foo
SET PATH=%PATH%;"C:\Some Folder With Spaces In It"
SET PATH=%PATH%;"C:\Program Files\Foobar Inc Software"
But never...
SET "PATH=%PATH%;SomePath"
Within my batch file I have a variable that contains a file path:
SET VAR1=C:\Folder1\Folder2\File.txt
I would like to extract on the directory structure and retreive:
C:\Folder1\Folder2\
I have read threads like this where I need to use %~dp0 where 0 I believe is passed as a parameter. I have tried %~dpVAR1 but that doesn't work. How can I get the output I'm looking for, but with a variable containing the file path?
Also, to make matters difficult, I have to perform all of this within an IF condition which means that once the variable is declared, I will need to refer to it with ! instead of % (I have declared setlocal enableextensions enabledelayedexpansion at the beginning of my script to allow for this).
Any help is much appreciated!
Thanks!
Andrew
You are attempting to use parameter expansion syntax on an environment variable - that cannot work. But it is relatively easy to do what you want.
Using a CALL (relatively slow):
(...
call :getPath "!var!" var
...
)
exit /b
:getPath
set "%2=%~dp1"
exit /b
Using FOR, assuming the variable does not contain any wildcards (fast)
(...
for %%F in ("!var!") do set "var=%%~dpF"
...
)
Using FOR, if the variable may contain wildcards (also fast)
(...
for /f "delims=" %%F in ("!var!") do set "var=%%~dpF"
...
)
Note 1: If the variable does not contain the full path, then all the solutions will attempt to resolve the name into an absolute path and will return the full absolute path. For example, if var contains foobar\test.txt, then the solutions will include the full path to the current directory, even if the file is not found. Something like c:\pathToCurrentDirectory\foobar\.
Note 2: All solutions above will remove all quotes from the path.
Note 3: A path could include the ! character, which will cause problems when expanding %~dp1 or %%~dpF because you have delayed expansion enabled. The delayed expansion will corrupt both ^ and ! if the value contains !. There is a solution that involves protecting both ! and ^. Here is a demonstration applied to the last solution above. The protection requires normal expansion, and since you are within a code block, it requires at least one CALL. It could be done without a subroutine, but it is easier with a subroutine. The subroutine assumes the variable is named var.
(...
call :getPath
...
)
exit /b
:getPath
set "var=!var:"=!"
set "var=!var:^=^^^^!"
set "var=%var:!=^^^!%" !
for /f "delims=" %%F in ("!var!") do set "var=%%~dpF" !
exit /b
I do believe (once again) many questions are on the same topic (string constraints, or splitting strings).
Instead of giving you the whole code, I'm going to give you a template and explain why %~dpVAR! didn't work.
Firstly, why %~dpVAR! did't work.
Before I get into modifiers, let's discuss parameters. You may know that batch files can parse parameters to each other. These parameters can be called by using a single percent sign (%) in front of the numbers 0-9. As far as I'm aware (someone might have made a way for more to be parsed), only 9 parameters can be parsed. You may think that is wrong (there's 10 parameters right?). Parameters 1-9 are parsed to the batch file (or function within one), %0 is the file path of the batch file (or function name). If you look, %~dp0 shares some (not really) resemblance to %0. This will be discussed below.
Secondly, the term %~dp0 has modifiers in it. Modifiers are things that modify variables (only in the case of parameters and those declared in for loops, you know the ones with double percent signs like %%i) and parameters. The modifier d expands the parameter to a drive letter only while p expands the parameter to a path only. You may think that these would contradict themselves, but parameters can be combined to create extremely wacky formats.
So, as you can see, you attempt at replacing 0 with your variable name failed because it's not specified for those sort of things.
Now, on to the template.
You can constrain variables (and put them into other variables) like this:
set variable=!variable:~offset,amount!
Don't worry if that seems confusing, I'm about to explain the components.
Firstly, notice that there is no /a switch. This is because this is not a mathematical function (don't really know why I added this). So, before I explain it, here's an example of what it would do to a variable name numbers that has the value of 0123456789.
set numbers=!numbers:~5,1!
By using that line of code, numbers would now equal 5. This is because it is recreating the variable with a smaller version of the original value (gee this is hard to explain). As you can see, there is a 5 where offset was on the template above. This is because it is skipping the first 5 characters and setting the variable as the next amount, or 1 character (I really hope you're getting this).
So basically, it sets a variable as a shorter value of a different (or the same) variable determined by the offset and the amount of characters to contain in it.
I really hope this helps because I probably wouldn't understand a word of this.
Can someone redirect this poor guy to a link explaining this better (I tried, ok!)?
Complete example of extracting paths from variable:
#echo off
set /p Fullpath="Specify full path: "
call :getPath %Fullpath% filename folder
echo %filename%
echo %folder%
pause
exit /b
:getPath
set "%2=%~nx1"
set "%3=%~dp1"
exit /b
Would this work:
SET VAR1=C:\Folder1\Folder2\File.txt
echo %var1%
Where Echo is the name of your exe.
%CD% may work as well: Echo %CD%
I need to start an application by the name of Acad.exe without knowing its full path. this path is namly decide upon instalation by the person installing the app.
how can i achive this?
It is common for applications to store their install location in the registry, so the preferred way of finding them would be to look up the appropriate place in the registry. That way, you won't accidentally start a different program with the same file name.
Assuming acad.exe is AutoCAD, this page gives the locations you have to look up.
Try this in any language that has an interface to the OLE2 layer:
CreateDispatch("Autocad.Application")
In C++:
::CoInitializeEx(NULL);
::CreateDispatch("AutoCAD.Application");
With a batch script:
Save the following under the name `start_autocad.vbs
set objShell=CreateObject("Autocad.Application")
objShell.Visible = TRUE
run cscript start_autocad.vbs.
If this acad.exe installation follows the Windows convention, the installation process creates a special key in the registry :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
If acad.exe is defined there with required parameters then it's possible to start it from a batch file with this simple command :
START acad.exe
No need to specify the complete path, Windows will get it from the corresponding AppPath entry.
Assuming it is on the C: drive, you could do:
#echo off
c:
cd \
for /f "delims=" %%a in ('dir /s /b acad.exe') do set exeLcn=%%a
start %exeLcn%
Instead of inventing your own method, you just might reproduce the behavior of CMD.EXE, looking for ACAD.EXE in the %PATH%.
try this as an example to get you started...
:inpath
echo %~$PATH:1
echo %~dp$PATH:1
goto :eof
invoke it this way
call :inpath acad.exe