I have an exe file, say, C:\Programs\tools\4.0.97869\program.exe
This, obviously, version number may vary, but I'm totally sure that it will always be 4.0.something
I can execute some command from batch file, specifying path to that exe like this:
"C:\Programs\tools\4.0.97869\program.exe" /option:Key somevalue
Which works perfectly fine. However, I would like to place a wildcard here, for example like this:
C:\Programs\tools\4.0.*\program.exe
Since I can perfectly navigate like this using cd
I do not want to specify that exe in Path
I do not want to cd to that directory and call program.exe from there
It there a way to specify first matching directory which has a
necessary file to execute in one line?
Thanks.
Here is a solution that uses a PowerShell script:
$pathPattern = 'C:\Programs\tools\4.0.*\program.exe'
if(!(Test-Path $pathPattern)){
throw "Could not find a single executable"
}
$paths = Get-Item -Path $pathPattern
Invoke-Expression $paths[0]
A PowerShell solution would be a better idea. If only cmd.exe can be used, the following might work. It is not a one-liner. Store this in a .bat file and CALL it. It works by running the first "program.exe" it can find. It tries to get the most recent one by ordering the directory search as most recent first.
SETLOCAL ENABLEDELAYEDEXPANSION
SET EXITCODE=0
SET "EXEWILD=C:\Programs\tools\4.0.*"
FOR /F %%d IN ("%EXEWILD%") DO (SET "EXEBASE=%%~dpd")
IF NOT EXIST "%EXEWILD%" (
ECHO ERROR: Tool directory "%EXEWILD%" does not exist.
SET EXITCODE=4
GOTO TheEnd
)
FOR /F "usebackq tokens=*" %%d IN (`DIR /B /O-D "%EXEWILD%"`) DO (
IF EXIST "%EXEBASE%\%%~d\program.exe" (
"%EXEBASE%%%~d\program.exe" %*
SET EXITCODE=!ERRORLEVEL!
GOTO TheEnd
) ELSE (
ECHO WARNING: program.exe not found in "%EXEBASE%\%%~d"
)
)
:TheEnd
EXIT /B %EXITCODE%
Related
I need to process a full path to a file, i.e. C:\fold1\fold2...\foldN\filename.dat in order to get the parent file folder and file name separately, that is root=C:\fold1\fold2...\foldN and file=filaname.dat.
This because I want to generalise a call to a program no matter from where you call the main .bat (this one), such to be able to perform a pushd to %root% before calling this specific program.
I am working at this, but I get stack when posing line=%%c:
: before remove quotes from %var%
set var=%var:"=%
if exist %temp% (
if exist %temp%\filepath.txt del %temp%\filepath.txt
echo %var% > %temp%\filepath.txt
)
for /f "tokens=1,* delims= " %%z in (%temp%\filepath.txt) do (
set line=%%z
echo Line writes %line%.
set root=empty
goto :processtoken
)
:processtoken
for /f "tokens=1,* delims=\" %%b in ("%line%") do (
echo %%b
echo %%c
set line=%%c rem this does not work, why??
echo Now line writes %line%
if "%root%"=="empty" (
echo [INFO] Root is empty, initialising..
echo %%c
set root=%%c
) else (
set root=%root%\%%c
)
echo Root is %root%
goto :end
)
The problem I am facing is that when I print to see how %line% has changed, it shows that %line% (after set line=%%c corresponds to the FULL PATH (while my intention is to recursively get to the file name, I still need to add the condition in finding the "\" string in %%c, when not present anymore it will mean we got to the final step, i.e. %root% will now correspond to the final root folder).
Thanks to who will try to help me resolving this issue.
EDIT:
this main program is called as follow:
prog arg1 arg2 arg3 arg4 arg5
arg5 is the path to the file which will internally be the argument for this other program. To be as general as possible I want to made functional this program no matter from where you call it. Still you have two options:
you are already in the folder containing the file to be processed by this internally called program, in such case arg5 will be passed as filename.dat (without quotes, except if it contains spaces)
you are not in the root folder so you pass directly the FULL PATH (in double quotes if it contains spaces).
The problem is in case 2, since this internal program works properly when you call it from root directory and you pass to it only FILENAME.DAT.
This is why I posed this question. %var% is simply arg5, in the ways I explained hereby.
I hope I have been a little more clear than before.
PS. I'm not an experienced programmer in all ways, so I do apologise if I miss in clearance and professionalism. The write/read from/to %temp% folder was just to exploit a newer way of programming in batch, nothing else. I knew it was superfluous.
since you mentioned set var=%5 in a comment:
set "root=%~dp5"
set "file=%~nx5"
should be all you need. See call /? for more details.
You should take a look at the for-variable modifiers (FOR /?)
root=%%~dpz is used to expand to: d=drive and p=path of z
file=%%~nxz is used to expand to: n=name and x=extension of z
Then you can change your block to
for /f "tokens=1,* delims= " %%z in (%temp%\filepath.txt) do (
set "line=%%z"
set "root=%%~dpz"
set "file=%%~nxz"
REM ** Show the variables
set root
set file
)
Another way to do this in a batch-file would be to use the PowerShell Split-Path command designed to do this.
SET "THEFILE=%TEMP%\file path.txt"
FOR /F "delims=" %%A IN ('powershell -NoLogo -NoProfile -Command
"""$([Convert]::ToChar(34))$(Split-Path -Path """%THEFILE%""")$([Convert]::ToChar(34)) $([Convert]::ToChar(34))$(Split-Path -Path """%THEFILE%""" -Leaf)$([Convert]::ToChar(34))"""') DO (
SET "FILESTRING=%%A"
)
SET "PATHPART="
FOR %%A IN (%FILESTRING%) DO (
IF NOT DEFINED PATHPART (SET "PATHPART=%%~A") ELSE (SET "FILEPART=%%~A")
)
ECHO PATHPART is "%PATHPART%"
ECHO FILEPART is "%FILEPART%"
Of course, it is easier if the script is written for PowerShell.
$TheFile = "$Env:TEMP\filepath.txt"
$PathPart = Split-Path -Path $TheFile
$FilePart = Split-Path -Path $TheFile -Leaf
UPDATE:
Actually, it appears that splitting the path is not needed to use PUSHD. Just add \.. to the end.
PUSHD C:\Users\joe\afile.txt\..
Normally I can specify a folder for a batch file to work in.
Not so with the FOR command:
for %%a in (G_*.txt) do ren "%%a" "test-%%a"
This finds G_*.txt in all files and renames those by putting test- in front of the filename.
I tried specifying G_*.txt further with C:\test\G_*.txt but that is not accepted.
I also tried pouring this into a variable but that also failed.
Who knows what to do?
Again, I had to change the approach: using SET /R will put a long path into the %%a variable which makes the idea unsuited to put a bit of text in front of a filename.
I found the solution by selecting the work directory first and let the FOR construction do its work. Since it must be network proof, I had to use the PUSHD command.
This is what the final result looks like:
set source=\\nassie\home\test\source with nasty space
set target=D:\target
PUSHD %source%
:: If this fails then exit
If %errorlevel% NEQ 0 goto:eof
for %%a in (G_*.txt) do xcopy "%source%\%%a" "%target%\textblabla_%%a*" /D /Y
POPD
I have some folders with different names. Each folder has a specific structure as listed below:
Folder1
Contents
x64
Folder1.aaxplugin
TransVST_Fixer.exe
Folder 2
Contents
x64
Folder 2.aaxplugin
TransVST_Fixer.exe
There are two files within each subfolder x64. One file has the same name as the folder two folder levels above. The other file is an .exe file whose name is the same in all folders.
Now I need to run file with file extension aaxplugin on each specific .exe file. It would be obviously very time consuming opening each and every single folder and drag & drop each file on .exe to run it on this file.
That's why I am trying to create a batch script to save some time.
I looked for solutions here on Stack Overflow. The only thing I have found so far was a user saying this: When I perform a drag & drop, the process 'fileprocessor.exe' is executed. When I try to launch this exe, though, CMD returns error ('not recognized or not batch file' stuff).
How can I do this?
UPDATE 12/22/2015
I used first a batch file with following line to copy the executable into x64 subfolder of Folder1.
for /d %%a in ("C:\Users\Davide\Desktop\test\Folder1\*") do ( copy "C:\Program Files\Sugar Bytes\TransVST\TransVST_Fixer.exe" "%%a\x64\" 2> nul )
After asking here, I tried the following script:
for /f "delims=" %%F in ('dir /b /s x64\*.aaxplugin') do "%%~dpFTransVST_Fixer.exe" "%%F"
Unfortunately, the output is as following
C:\Users\Davide\Desktop>for /F "delims=" %F in ('dir /b /s x64\*.aaxplugin') do "%~dpFTransVST_Fixer.exe" "%F"
The system cannot find the file specified.
Try the following batch code:
#echo off
setlocal EnableDelayedExpansion
for /R "%USERPROFILE%\Desktop\test" %%F in (*.aaxplugin) do (
set "FilePath=%%~dpF"
if not "!FilePath:\x64\=!" == "!FilePath!" "%ProgramFiles%\Sugar Bytes\TransVST\TransVST_Fixer.exe" "%%F"
)
endlocal
The command FOR with option/R searches recursive in all directories of directory %USERPROFILE%\Desktop\test being expanded on your machine to C:\Users\Davide\Desktop for files with file extension aaxplugin. The loop variable F contains on each loop run the name of the found file with full path without surrounding double quotes.
The drive and path of each found file is assigned to environment variable FilePath.
Next a case-sensitive string comparison is done between file path with all occurrences of string \x64\ case-insensitive removed with unmodified file path.
Referencing value of environment variable FilePath must be done here using delayed expansion because being defined and evaluated within a block defined with ( ... ). Otherwise command processor would expand %FilePath% already on parsing the entire block resulting in a syntax error on execution because string substitution is not possible as no environment variable FilePath defined above body block of FOR loop.
The strings are not equal if path of file contains a folder with name x64. This means on provided folder structure that the file is in folder x64 and not somewhere else and therefore the application is executed next from its original location to fix the found *.aaxplugin file.
The line with IF is for the folder structure example:
if not "C:\Users\Davide\Desktop\test\Folder1\Contents" == "C:\Users\Davide\Desktop\test\Folder1\Contents\x64\"
if not "C:\Users\Davide\Desktop\test\Folder 2\Contents" == "C:\Users\Davide\Desktop\test\Folder 2\Contents\x64\"
So for both *.aaxplugin files the condition is true because the compared strings are not identical
Also possible would be:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%F in ('dir /A-D /B /S "%USERPROFILE%\test\*.aaxplugin" 2^>nul') do (
set "FilePath=%%~dpF"
if not "!FilePath:\x64\=!" == "!FilePath!" "%ProgramFiles%\Sugar Bytes\TransVST\TransVST_Fixer.exe" "%%F"
)
endlocal
But command DIR is not really necessary as it can be seen on first provided code.
But if the application TransVST_Fixer.exe for some unknown reason does its job right only with directory of file being also the current directory, the following batch code could be used instead of first code using the commands pushd and popd:
#echo off
setlocal EnableDelayedExpansion
for /R "%USERPROFILE%\test" %%F in (*.aaxplugin) do (
set "FilePath=%%~dpF"
echo !FilePath!
if /I "!FilePath:~-5!" == "\x64\" (
pushd "%%~dpF"
"%ProgramFiles%\Sugar Bytes\TransVST\TransVST_Fixer.exe" "%%~nxF"
popd
)
)
endlocal
There is one more difference in comparison to first code. Now the last 5 characters of path of file are compared case-insensitive with the string \x64\. Therefore the file must be really inside a folder with name x64 or X64. A folder with name x64 or X64 anywhere else in path of file does not result anymore in a true state for the condition as in first two batch codes.
But if for some unknown reason it is really necessary to run the application in same folder as the found *.aaxplugin and the directory of the file must be the current directory, the following batch code could be used:
#echo off
setlocal EnableDelayedExpansion
for /R "%USERPROFILE%\test" %%# in (*.aaxplugin) do (
set "FilePath=%%~dp#"
if /I "!FilePath:~-5!" == "\x64\" (
pushd "%%~dp#"
"%%~dp#TransVST_Fixer.exe" "%%~nx#"
popd
)
)
endlocal
The path of the file referenced with %%~dpF always ends with a backslash which is the reason why there is no backslash left of TransVST_Fixer.exe (although command processor could handle also file with with two backslashes in path).
In batch code above character # is used as loop variable because %%~dp#TransVST_Fixer.exe is easier to read in comparison to %%~dpFTransVST_Fixer.exe. It is more clear for a human with using # as loop variable where the reference to loop variable ends and where name of application begins. For the command processor it would not make a difference if loop variable is # or upper case F.
A lower case f would work here also as loop variable, but is in general problematic as explained on Modify variable within loop of batch script.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
dir /?
echo /?
endlocal /?
for /?
if /?
popd /?
pushd /?
set /?
setlocal /?
Your question isn't quite clear, but it seems, something like this should work:
for /f "delims=" %%f in ('dir /b /s X64\*.ext') do "%%~dpfMyExe.exe" "%%f"
Maybe you have to change directory to each folder (depends on your .exe):
for /f "delims=" %%d in ('dir /B /ad') do (
pushd "%%d"
for /f "delims=" %%f in ('dir /b "contents\x64\*.ext"') do (
cd Contents\x64
MyExe.exe "%%f"
)
popd
)
Assuming:
The Directory structure is fixed and the files are indeed in a subfolder contents\X64\.
MyExe.exe is the same (name) in every folder.
There is only one file *.ext in every folder.
I'll give you the script I created for doing so, hope it works for you
for /d %%d IN (./*) do (cd "%%d/Contents/x64" & "../../../TransVST_Fixer.exe" "%%d" & cd "/Program Files (x86)\Common Files\Avid\Audio\Plug-Ins")
Please note that I placed the fixer inside the root folder so I just have to copy it once. You have to place it inside your root folder and execute it. What it does:
iterate over each folder
for each one it enters /Contents/x64, executes the fixer (wich is 3 levels above) and after that returns to the original folder.
If you have your plugins in a different folder, you just have to change this part replacing the path for the one you have your plugins in.
cd "/Program Files (x86)\Common Files\Avid\Audio\Plug-Ins"
REMEMBER to place the script on that folder. For this example I place my script on the folder "/Program Files (x86)\Common Files\Avid\Audio\Plug-Ins" and run it (as a .bat).
PS: the fixer will place the fixed plugins in "C:\Users\Public\modified" (just read the screen while executing, it gives you the new files path. If you want to move them to the right path, you can execute this from the new files path ("C:\Users\Public\modified")
for %%d IN (*.aaxplugin) do (mkdir "%%d_temp/Contents\x64" & move "%%d" "%%d_temp/Contents\x64/%%d" & rename "%%d_temp" "%%d")
with that, I iterate over every plugin and create a folder with the same name (I create _temp because of name colision, after moving the file I rename it to the correct one), also with the subfolder "/Contents/x64", and move the plugin inside. Once donde, you can just take the resulting folders and place them in their correct path.
Hope it works, for me it works like a charm.
I need to write a script to work in Windows, that when executed will run a command in some of sub-directories, but unfortunately I have never done anything in batch, and I don't know where to start.
With the example structure of folders:
\root
\one
\two
\three
\four
I want the script to enter the specified folders (e.g. only 'one' and 'four') and then run some command inside every child directories of that folders.
If you could provide any help, maybe some basic tutorial or just names of the commands I will need, I would be very grateful.
You can tell the batch to iterate directories:
for /d %i in (C:\temp\*) do ( cd "%i" & *enter your command here* )
Use a percent sign when run directly on the command line, two when run from a batch
In a batch this would look something like this:
#echo off
set back=%cd%
for /d %%i in (C:\temp\*) do (
cd "%%i"
echo current directory:
cd
pause
)
cd %back%
Put the commands you need in the lines between ( and ).
If you replace C:\temp\ with %1 you can tell the batch to take the value of the directory from the first parameter when you call it.
Depending of the amount of directories you then either call the batch for each directory or read them from a list:
for /f %i in (paths.lst) do call yourbatch %i
The paths.lstwill look like this:
C:\
D:\
Y:\
C:\foo
All of this is written from memory, so you might need to add some quotations marks ;-)
Please note that this will only process the first level of directories, that means no child folders of a selected child folder.
You should take a look at this. The command you are looking for is FOR /R. Looks something like this:
FOR /R "C:\SomePath\" %%F IN (.) DO (
some command
)
I like answer of Marged that has been defined as BEST answer (I vote up), but this answer has a big inconvenience.
When DOS command between ( and ) contains some errors, the error message returned by DOS is not very explicit.
For information, this message is
) was unexpected at this time.
To avoid this situation, I propose the following solution :
#echo off
pushd .
for /d %%i in (.\WorkingTime\*.txt) do call :$DoSomething "%%i"
popd
pause
exit /B
::**************************************************
:$DoSomething
::**************************************************
echo current directory: %1
cd %1
echo current directory: %cd%
cd ..
exit /B
The FOR loop call $DoSomething "method" for each directory found passing DIR-NAME has a parameter. Caution: doublequote are passed to %1 parameter in $DoSomething method.
The exit /B command is used to indicate END of method and not END of script.
The result on my PC where I have 2 folders in c:\Temp folder is
D:\#Atos\Prestations>call test.bat
current directory: ".\New folder"
current directory: D:\#Atos\Prestations\New folder
current directory: ".\WorkingTime"
current directory: D:\#Atos\Prestations\WorkingTime
Press any key to continue . . .
Caution: in Margeds answer, usage of cd "%%i" is incorrect when folder is relative (folder with . or ..).
Why, because the script goto first folder and when it is in first folder it request to goto second folder FROM first folder !
On Windows 10 and later, it should be like this:
#echo off
for /D %%G in ("C:\MyFolderToLookIn\*") DO (
echo %%~nxG
)
This will show the name of each folder in "C:\MyFolderToLookIn". Double quotes are required.
If you want to show full path of the folder, change echo %%~nxG with echo %%G
I'm looking for a simple way to test if an executable exists in the PATH environment variable from a Windows batch file.
Usage of external tools not provided by the OS is not allowed. The minimal Windows version required is Windows XP.
Windows Vista and later versions ship with a program called where.exe that searches for programs in the path. It works like this:
D:\>where notepad
C:\Windows\System32\notepad.exe
C:\Windows\notepad.exe
D:\>where where
C:\Windows\System32\where.exe
For use in a batch file you can use the /q switch, which just sets ERRORLEVEL and doesn't produce any output.
where /q myapplication
IF ERRORLEVEL 1 (
ECHO The application is missing. Ensure it is installed and placed in your PATH.
EXIT /B
) ELSE (
ECHO Application exists. Let's go!
)
Or a simple (but less readable) shorthand version that prints the message and exits your app:
where /q myapplication || ECHO Cound not find app. && EXIT /B
for %%X in (myExecutable.exe) do (set FOUND=%%~$PATH:X)
if defined FOUND ...
If you need this for different extensions, just iterate over PATHEXT:
set FOUND=
for %%e in (%PATHEXT%) do (
for %%X in (myExecutable%%e) do (
if not defined FOUND (
set FOUND=%%~$PATH:X
)
)
)
Could be that where also exists already on legacy Windows versions, but I don't have access to one, so I cannot tell. On my machine the following also works:
where myExecutable
and returns with a non-zero exit code if it couldn't be found. In a batch you probably also want to redirect output to NUL, though.
Keep in mind
Parsing in batch (.bat) files and on the command line differs (because batch files have %0–%9), so you have to double the % there. On the command line this isn't necessary, so for variables are just %X.
Here is a simple solution that attempts to run the application and handles any error afterwards.
file.exe /? 2> NUL
IF NOT %ERRORLEVEL%==9009 ECHO file.exe exists in path
Error code 9009 usually means file not found.
The only downside is that file.exe is actually executed if found (which in some cases is not desiderable).
This can be accomplished via parameter substitution.
%~$PATH:1
This returns the full path of the executable filename in %1, else an empty string.
This does not work with user-defined variables. So if the executable filename is not a parameter to your script, then you need a subroutine. For example:
call :s_which app.exe
if not "%_path%" == "" (
"%_path%"
)
goto :eof
:s_which
setlocal
endlocal & set _path=%~$PATH:1
goto :eof
See http://ss64.com/nt/syntax-args.html
For those looking for a PowerShell option. You can use the Get-Command cmdlet passing two items. First give the current dir location with .\ prefixed, then give just the exe name.
(Get-Command ".\notepad", "notepad" -ErrorAction Ignore -CommandType Application) -ne $null
That will return true if found local or in system wide paths.
#echo off
set found=
set prog=cmd.exe
for %%i in (%path%) do if exist %%i\%prog% set found=%%i
echo "%found%"
if "%found%"=="" ....
Sometimes this simple solution works, where you check to see if the output matches what you expect. The first line runs the command and grabs the last line of standard output.
FOR /F "tokens=*" %%i in (' "xcopy /? 2> nul" ') do SET xcopyoutput=%%i
if "%xcopyoutput%"=="" echo xcopy not in path.
Use command : powershell Test-Path "exe which you looking for"
It will return True if its present, otherwise False.