Batch file launcher (in batch) does not evaluate the path correctly - batch-file

Task
I'm trying to make a launcher (in batch) for my (almost) all batch program in the Program Files (x86) folder. I also need it to be compatible with any one's Windows 10 machine.
Problem
The cd command does not evaluate the path correctly. It just says The directory name is invalid.
Code
#echo off
color 02
cls
cd "%~dp0batchfile.bat"
start /max /realtime %cd%
exit
Debug
I've moved the file around, adjusted the code, and even put the raw path and it all worked! But it still gives the error and causes the program to malfunction. I'm worried it can cause damage to someone's computer and to others' as well. (It's pretty overcomplicated for batch lol) I've also tried to echo the cd before, the path it's supposed to be set to, and the cd after. The cd before and what it's to be set to is fine, but it still says The directory name is invalid. And "doesn't" get set. Here is the code I tried:
#echo off
color 02
cls
cd "%~dp0batchfile.bat"
rem Debug:
echo %cd%
pause
rem real code:
start /max /realtime %cd%
exit
And it's fine. It gives the error, yet it works without interruption and causes less lag and glitches. I haven't a clue why because for my smol phatt brain this shouldn't even be possible. Bless you. Maybe it's just a bug that Microsoft needs to fix, but whatever it is, it is so annoying and it ruins the cleanliness of my (almost) all batch program.
Conclusion (That you made)
You think: This imbecile doesn't even know where to START when asking questions on StackOverflow.
Me reading your thoughts: This is the first question I ask on StackOverflow. So yes, I mean no, no yes, wait wait no, I don't know where to start. What? Bless you. Oop Bless you again.

cd "%~dp0batchfile.bat"
Why are you trying to cd into a batch file in the same directory as your script? That's obviously not going to work, as per the transcript you'll see idf you let cmd echo the commands before executing them (by commenting out the echo off, along with other stuff not needed for debugging):
rem #echo off
rem color 2
rem cls
cd "%~dp0batchfile.bat"
rem start /max /realtime %cd%
rem exit
If you run that, you'll see:
C:\Users\Allan>rem #echo off
C:\Users\Allan>rem color 02
C:\Users\Allan>rem cls
C:\Users\Allan>cd "C:\Users\Allan\batchfile.bat"
The system cannot find the path specified.
C:\Users\Allan>rem start /max /realtime C:\Users\Allan
C:\Users\Allan>rem exit
In other words, you probably should just be using %~dp0 in the cd command.

batchfile.bat is a FILE and not a directory. So it is not possible to change current directory to file batchfile.bat.
One solution is cd /D "%~dp0" to change the current directory to directory containing the currently executed batch file. This works as long as the batch file is stored on a storage media with a drive letter assigned. So the code would be:
#echo off
cd /D "%~dp0"
color 02
cls
start "Window Title" /MAX "FileName.exe"
color
But a batch file could be stored also on a network resource started with using UNC path. Windows command processor cmd.exe does not allow by default that a network resource path is set as current directory. The following code would be necessary for such use cases.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0" || goto :EOF
color 02
cls
start "Window Title" /MAX "FileName.exe"
color
popd
But a launcher batch file should not make the directory of itself the current directory, but the directory of the batch file to launch of which file name is passed to the launcher batch file as first argument. So a launcher batch file for other batch files could be:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" (
color 02
cls
echo INFO: "%~nx0" must be started with a batch file name.
echo/
pause
color
) else start "%~n1" /MAX %SystemRoot%\System32\cmd.exe /C "pushd "%~dp1" && (color 02 & cls & "%~nx1" & popd)"
endlocal
Do not use command exit to exit cmd.exe independent on calling hierarchy. That is not necessary here and makes it only more difficult to debug the batch file on executing it from within a command prompt window.
Do not use option /REALTIME of command START because this process priority class is mainly for drivers and should never be used for other executables and definitely not for a batch file processed by cmd.exe.
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.
call /? explaining:
%~dp0 ... drive and path of argument 0, the full batch file path always ending with a backslash,
%~1 ... first argument with surrounding double quotes removed,
%~n1 ... file name of first argument,
%~dp1 ... drive and path of first argument,
%~nx1 ... file name and extension of first argument.
cd /?
cls /?
cmd /?
color /?
echo /?
endlocal /?
goto /?
if /?
pause /?
popd /?
pushd /?
setlocal /?
start /?
See also single line with multiple commands using Windows batch file.

Related

How to store Parent Directory as a variable, Cmd/Bat files

Problem
I am writing some .cmd / .bat files on a windows machine that need to work on an sd card with variable parent directories. The sd card will likely change drive names (Drive A, Drive E, etc.) as it moves from device to device and I want to write cmd files that will anticipate that. I would like this to work with my linux steam deck if possible, but if not I understand.
Rom Location
E:\Games\Nintendo\3DS\Games\Animal Crossing New Leaf.3ds
Core Location
E:\Games\RetroArch\cores\citra_libretro.dll
3DS.cmd , currently works at this address
#echo off
echo Keeping Window Active for GOG Time Tracking
cd "E:\Games\RetroArch\"
"retroarch.exe" -L "cores\citra_libretro.dll" %1 -f
Animal Crossing New Leaf.cmd , currently works at this address
#echo off
call "3DS.cmd" "E:\Games\Nintendo\3DS\Games\Animal Crossing New Leaf.3ds"
Question
How would I write the code above as a windows file on any non-specific drive directory where the current Directory is on the Drive named E:\ ?
(Ex: A:\ , or B:, and so on)
There can be used the following lines in the batch file 3DS.cmd if this batch file is stored in root of the SD card and is executed from the SD card mounted as drive with a drive letter:
#echo off
echo Keeping window active for GOG time tracking
cd /D "%~d0\Games\RetroArch"
retroarch.exe -L cores\citra_libretro.dll %1 -f
The usage help of command CALL output on running call /? in a command prompt window explains how to reference the arguments of a batch file. There is always the argument 0 even on batch file is executed without any argument string passed to the batch file by a user or another process.
%0 references the string used to start the execution of the batch file. On double clicking on a batch file stored on an SD card mounted with a drive letter by Windows, %0 expands to the fully qualified file name of the batch file on the SD card enclosed in " because of the Windows File Explorer starts in background:
C:\WINDOWS\system32\cmd.exe /c ""Animal Crossing New Leaf.cmd" "
The usage help of the Windows Command Processor cmd.exe output on running cmd /? explains how the arguments are interpreted by cmd.exe in this case. The first and the last " are removed from the command line. The started cmd.exe executes therefore:
"E:\Animal Crossing New Leaf.cmd"
That string with the double quotes is argument 0 of the executed batch file.
%~d0 can be used in the batch file to reference just the drive letter and the colon of the currently running batch file respectively \\ if the batch file is stored on a network resource executed using its UNC path.
The code above works only for batch file being stored in root of a storage media mounted with a drive letter.
A code for 3DS.cmd working always independent on which storage media the batch file is stored and in which directory and how the batch file is started as long as the directory Games is a subdirectory of the directory containing the batch file is:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
if "%~1" == "" echo ERROR: %~nx0 called without game file name!& pause & exit /B
pushd "%~dp0Games\RetroArch"
echo Keeping window active for GOG time tracking
retroarch.exe -L cores\citra_libretro.dll %1 -f
popd
endlocal
%~dp0 expands to full path of the batch file always ending with a backslash.
See also: What is the reason for batch file path referenced with %~dp0 sometimes changes on changing directory? The bug of cmd.exe does not matter here because of %~dp0 is used before changing the current directory the first time with the command PUSHD.
The batch file Animal Crossing New Leaf.cmd stored in same directory as 3DS.cmd should contain only the single command line:
#call "%~dp03DS.cmd" "%~dp0Games\Nintendo\3DS\Games\Animal Crossing New Leaf.3ds"
The two batch files can be used with these improvements also on copying all directories and files on the SD card to a directory of user´s choice like %UserProfile%\RetroGames.
It is also possible to use only one batch file with name Animal Crossing New Leaf.cmd stored in the directory with the subdirectory Games and all the other directories and files with the following lines:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Games\RetroArch" || (echo ERROR: Missing subdirectory "Games\RetroArch"& pause & exit /B)
echo Keeping window active for GOG time tracking
retroarch.exe -L cores\citra_libretro.dll "%~dp0Games\Nintendo\3DS\Games\Animal Crossing New Leaf.3ds" -f
popd
endlocal
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /?
cd /?
echo /?
endlocal /?
exit /?
if /?
pause /?
popd /?
pushd /?
setlocal /?
I used Chat GPT, it gave me this result that is now working.
3ds.cmd
#echo off
echo Keeping Window Active for GOG Time Tracking
set sd_dir=%cd:~0,1%
cd "%sd_dir%:\Games\RetroArch\"
"retroarch.exe" -L "cores\citra_libretro.dll" %1 -f
Animal Crossing New Leaf.cmd
#echo off
set sd_dir=%cd:~0,1%
call "3DS.cmd" "%sd_dir%:\Games\Nintendo\3DS\Games\Animal Crossing New Leaf.3ds"

How can I find my error in the batch script?

The code below should archive some files by moving them into a subfolder. The batch file asks the user for the folder path. Then a subfolder should be created and if that was successful, it should move all files in the user input directory into the subdirectory. It works, but it closes although using pause. It does not output anything about a syntax error or anything at all. Please let me know if somebody notices something.
#echo off
SETLOCAL EnableDelayedExpansion
echo Insert path:
set /p path=
echo the path is %path%
cd %path%
echo The files will be moved to a new folder
pause
mkdir %path%\archived_files
IF EXIST "archived_files" (
for /f %%A in ('DIR /A /D /B') do (
echo %%A && move /Y %path%\%%A %path%\archived_files)
echo Folder "archived_files" created or already exists
) else ( echo Folder "archived_files" does not exist )
echo the files have been transferred
pause
ENDLOCAL
I suggest to use this batch file for the file moving task.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BatchFileName=%~nx0"
set "BatchFilePath=%~dp0"
set "UserPath=%~1"
if defined UserPath goto ChangeFolder
:UserPrompt
set "UserPath="
set /P "UserPath=Enter path: "
rem Has the user not entered a string?
if not defined UserPath goto UserPrompt
rem Remove all double quotes from input string.
set "UserPath=%UserPath:"=%"
rem Has the user entered just one or more double quotes?
if not defined UserPath goto UserPrompt
:ChangeFolder
pushd "%UserPath%" 2>nul || (echo Folder "%UserPath%" does not exist.& goto UserPrompt)
for /F "eol=| delims=" %%I in ('dir /A-D /B 2^>nul') do goto CreateSubfolder
echo The folder does not contain any file to archive.& goto EndBatch
:CreateSubfolder
md "archived_files" 2>nul
if not exist "archived_files\" echo Failed to create subfolder: "archived_files"& goto EndBatch
rem It must be avoided that the currently running batch file is moved too.
set "ExcludeFileOption="
for %%I in ("%UserPath%\") do set "CurrentFolderPath=%%~dpI"
if "%CurrentFolderPath%" == "%BatchFilePath%" set "ExcludeFileOption= /XF "%BatchFileName%""
rem The command MOVE used with wildcard * does not move hidden files. A FOR loop
rem with MOVE is slow in comparison to usage of ROBOCOPY to move really all files.
rem The ROBOCOPY option /IS can be removed to avoid moving same files existing
rem already in the subfolder archived_files from a previous batch execution.
echo The files are moved to a new folder.
%SystemRoot%\System32\robocopy.exe . archived_files%ExcludeFileOption% /MOV /R:2 /W:5 /IS /NDL /NFL /NJH /NJS
if not errorlevel 2 if errorlevel 1 echo All files are moved successfully.
:EndBatch
popd
endlocal
pause
The batch file can be started with a a folder path as argument. So it is possible to right click on the batch file and click in opened context menu in submenu Send to on item Desktop (create shortcut). The .lnk file created on the user´s desktop can be renamed now also via context menu or key F2 to whatever name is useful like Archive files. Then the shortcut file can be cut with Ctrl+X and pasted with Ctrl+V in the folder %APPDATA%\Microsoft\Windows\SendTo to have in Send to context submenu the menu item Archive files. This makes it possible to right click on a folder and click in opened context menu in submenu Send to on Archive files to run the batch file without the need to enter a folder path manually.
The batch file prompts the user for the path if not started with a folder path as first argument or the folder cannot be found at all. This user prompt is done using a safe method. The batch file makes the passed or entered folder temporarily the current folder for the remaining commands using PUSHD and POPD instead of CD to work also with UNC paths.
There is checked next if the folder contains any file at all. Otherwise the user is informed that the directory does not contain files to archive and batch file ends without any further action.
The file movement is done with ROBOCOPY for the reasons described in a remark in the batch file which requires Windows Vista or a newer Windows version or Windows Server 2003 or a newer Windows server version.
I recommend to see also:
Debugging a batch file which answers your question.
What is the reason for "X is not recognized as an internal or external command, operable program or batch file"? It explains why path as name for the environment variable to assign the user entered path is a really bad idea.
How to stop Windows command interpreter from quitting batch file execution on an incorrect user input? It explains the reasons for using the additional code to evaluate the string entered by the user.
Why is no string output with 'echo %var%' after using 'set var = text' on command line? It explains the recommended syntax for the (re)definition of an environment variable and why using this syntax.
Syntax error in one of two almost-identical batch scripts: ")" cannot be processed syntactically here describes several common issues made by beginners in batch file coding like not enclosing a file/folder path in double quotes.
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.
call /? ... explains %~nx0, %~dp0 and %~1 whereby argument 0 is always the batch file itself.
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
pause /?
popd /?
pushd /?
rem /?
robocopy /?
set /?
setlocal /?
Other useful documentations used to write this code:
single line with multiple commands using Windows batch file
the Microsoft documentations for the used Windows Commands
the SS64 documentations for the used Windows CMD commands, especially:
ROBOCOPY.exe
ROBOCOPY Exit Codes
the Microsoft documentation about Using command redirection operators
and the SS64 documentation How-to: Redirection
Note: The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background with %ComSpec% /c and the command line within ' appended as additional arguments.

How to change the code of a batch file so that it can be executed from a network resource instead of root of a drive?

I need that code to run directly from the computer (shared network folder) and not from a USB drive which is mandatory as it can be seen in the code.
set pathToScript=%~d0
set pathToRessources=%~d0\Resources
set pathToGPO=%~d0\TemplateGPO
cscript //B "%windir%\system32\slmgr.vbs" /ato
mkdir "%WINDIR%\display"
How to change the code of this batch file so that it can be executed from a directory on a network resource instead of root of a drive?
The batch file must be run from root of a drive because of the command lines:
set pathToScript=%~d0
set pathToRessources=%~d0\Resources
set pathToGPO=%~d0\TemplateGP
Those three lines should be changed to:
set "pathToScript=%~dp0"
set "pathToRessources=%~dp0Resources"
set "pathToGPO=%~dp0TemplateGPO"
Then the batch files can be successfully executed from any directory of a local drive. But the batch file would still fail to execute successfully from a directory of a network resource accessed using a UNC path because of the command line:
cd /d "%pathToScript%"
Windows prevents by default that a directory on a network resource accessed with a UNC path is set as current directory for compatibility reasons for executables not supporting that the current directory is a directory of which path does not start with a drive letter and a colon. The solution is using pushd and popd instead of cd.
The modified batch file which can be executed from really any directory with local administrative privileges:
#echo off
rem RUN AS ADMIN
setlocal EnableExtensions DisableDelayedExpansion
set "pathToScript=%~dp0"
set "pathToRessources=%~dp0Resources"
set "pathToGPO=%~dp0TemplateGPO"
%SystemRoot%\System32\cscript.exe //B "%SystemRoot%\System32\slmgr.vbs" /ato
mkdir "%SystemRoot%\display"
copy /y "%pathToRessources%\Teneris-Background-Bilingual.jpg" "%PUBLIC%\Pictures\"
copy /y "%pathToRessources%\ssText3d.scr" "%PUBLIC%\Pictures\"
copy /y "%pathToRessources%\Teneris-Lock-Bilingual.jpg" "%SystemRoot%\display"
pushd "%pathToScript%"
lgpo.exe /g "%pathToGPO%\{A213BE3C-CA6C-4F72-9235-FC52719EB79F}"
popd
endlocal
%SystemRoot%\System32\shutdown.exe /r /t 15 /c "post config shutdown in less than 30 sec"
The three lines
pushd "%pathToScript%"
lgpo.exe /g "%pathToGPO%\{A213BE3C-CA6C-4F72-9235-FC52719EB79F}"
popd
can be perhaps changed to just
"%pathToScript%lgpo.exe" /g "%pathToGPO%\{A213BE3C-CA6C-4F72-9235-FC52719EB79F}"
if the executable lgpo.exe can be executed also from a directory different to its storage location which would make the batch file faster and would be also better if the batch file is executed using a group policy or a scheduled task.
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.
call /? ... explains %~d0 which references the drive of argument 0, i.e. the drive on which batch file is stored respectively the network resource on starting the batch file using a UNC path and %~dp0 which references drive and path of argument 0 which is the full qualified path of the batch file always ending with a backslash.
cscript /?
echo /?
endlocal /?
mkdir /?
popd /?
pushd /?
rem /?
set /?
setlocal /?
shutdown /?
I don't know if lgpo.exe supports also executing it with /? as parameter to get displayed the help of this application.

Is it a best practice to change the current work directory in the batch file?

I have a batch file where I call one application by passing certain arguments.
Here is the content of my sample.batch file
cd C:\Program Files\MyApplication
"C:\Program Files\MyApplication\Application.exe" -e -p "Application Projects/Testing/General/Notepad" -rm "Close Notepad"
Here I need to change my current working directory to the folder where Application.exe lives so that It finds certain required JARs which is located in that folder.Is it best practice to do so?Changing the current work directory before executing a batch file.
Try
pushd C:\Program Files\MyApplication
...
popd
as another choice.
best practice is a matter of both opinion and circumstance
One method is what Magoo and Stephan have suggested already:
pushd "%ProgramFiles%\MyApplication"
rem Other commands.
popd
This solution works even for UNC paths with command extensions enabled as by default as PUSHD temporarily maps the network share to a drive letter and makes this network drive the current directory.
Another solution is using SETLOCAL, CD and ENDLOCAL which has the advantage of all modifications of environment variables, command extensions and delayed expansion done by the commands between setlocal and endlocal don't matter for the commands after endlocal command line.
setlocal EnableExtensions DisableDelayedExpansion
cd /D "%ProgramFiles%\MyApplication"
rem Other commands running in their own environment, here running with command
rem extensions explicitly enabled and delayed expansion explicitly disabled.
endlocal
The command ENDLOCAL restores also the current directory as being set on execution of SETLOCAL.
See the answer on change directory command cd ..not working in batch file after npm install with more details about PUSHD (push directory), POPD (pop directory), SETLOCAL (setup local environment), CD (change directory) and ENDLOCAL (end local environment).
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.
cd /?
endlocal /?
rem /?
setlocal /?
popd /?
pushd /?

How to get name off current directory

I have been wanting my script to find a folder that starts with the string "onedrive...".
My code looks like this,
#echo off
set path="C:\Users\%USERNAME%"
if exist %path% (
cd "%path%\onedrive*"
echo %cd%
cd
)
pause
and the output I get is,
C:\Users\310176421\Backupscript\source
C:\Users\310176421\OneDrive for Business
where the first one is my .bat file directory and the second one is the line i want to make into a variable.
Any ideas?
Oh man don't do this, you are overwriting the system PATH. You have to use another name for that variable. And also you have to set it as local.
#echo off
SETLOCAL
REM blah blah
set _my_custom_path=....
ENDLOCAL
Here is a simple batch code which searches in profile directory of current user for a directory starting with string OneDrive and assigns the full path of first found folder without quotes to an environment variable output next before exiting batch.
#echo off
for /F "delims=" %%I in ('dir /AD /B /S "%USERPROFILE%\OneDrive*" 2^>nul') do (
set "OneDriveFolder=%%~I"
goto FoundFolder
)
echo Could not find a folder OneDrive.
goto :EOF
:FoundFolder
echo Found folder: %OneDriveFolder%
set "OneDriveFolder="
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 /?
for /?
goto /?
set /?
Note 1: C:\Users\%USERNAME% is not always equal %USERPROFILE% as the profile directory can be also on another drive than drive C: and Users is just the default parent directory for the user profiles on Windows Vista and later.
Note 2: 2^>nul redirects error message output by command DIR to stdout to device nul which means suppressing the error message in case of no directory starting with OneDrive is found case-insensitive. ^ escapes redirection operator > for command FOR to get 2>nul applied on command DIR.

Resources