batch file directory expansion - batch-file

I am trying to write a batch file that exists in an arbitrary directory and will create a new directory two levels above it. For instance, the batch file here:
w:\src\project\scripts\setup.bat
would create:
w:\src\project.build
I can't seem to figure out how to expand a path. Here is what I am currently doing:
#set SCRIPT_DIR=%~dp0
#set ROOT_DIR=%SCRIPT_DIR%\..
echo ROOT DIR: %ROOT_DIR%
#set ROOT_DIR_NAME=%ROOT_DIR:~0,-1%
#echo ROOT DIR NAME: %ROOT_DIR_NAME%
And this produces:
ROOT DIR: w:\src\w_dev1\scripts\\..
ROOT DIR NAME: w:\src\w_dev1\scripts\\.
What I wanted to do, was get ROOT_DIR_NAME to be the directory itself (without the trailing slash). I know I could hack this and switch the -1 to account for the '..', but is there not a cleaner way to handle this?

Your line #set ROOT_DIR_NAME=%ROOT_DIR:~0,-1% removes ony 1 character from the variable value. You want to remove more ('scripts' has 7, plus you have these '\..' at the end...). Are you sure that you always have 'scripts' as the last directory in the path where you install?
Anyway, if you use #set ROOT_DIR_NAME=%ROOT_DIR:~0,-11% it should work for your particular example.
However, I would suggest you use a more generic approach:
#set SCRIPT_DIR=%~dp0
#pushd %script_dir%
#pushd ..\..
#echo. current directory now is %cd%
#set root_dir=%cd%
#popd
#popd
This works independently of the length of your directory names.
The pushd commands change directories (and remember where it came from) -- the popd commands go back to what was remembered by pushd. The %cd% variable holds the current drive+path.

You can use the for command to resolve a relative path into a canonical absolute path:
#echo off
set "SCRIPT_DIR=%~dp0"
for %%P in ("%SCRIPT_DIR%..\..") do set "ROOT_DIR_NAME=%%~fP"
This script will set ROOT_DIR_NAME to the grand parent directory of the script.

Or a local subroutine
call :setvartopath ROOT_DIR_NAME "%~dp0\..\.."
goto :eof
:setvartopath
set %1=%~f2

Related

Batch file to read the root directory it is in

Is there a batch command that can read a root directory without the entire path?
I want a batch file to tell me if its in D:\ or E:\.
I tried to use:
set mypath=%cd%
#echo %mypath%
Pause
But it just says the exact place it is in rather than just the root.
Here's a few options for you which provide the root directory of the scripts current directory:
Using PushD/Popd
PushD\&Call Set "RootDir=%%CD%%"&PopD
Echo(%RootDir%
Pause
Using a For loop
For %%A In (%CD%) Do Set "RootDir=%%~dA\"
Echo(%RootDir%
Pause
Using variable expansion
Set "RootDir=%CD:~,3%"
Echo(%RootDir%
Pause
Edit
After reading your question again, I decided to add a fourth example. This one unlike the other three provides the root directory of the batch files location.
Set "RootDir=%~d0\"
Echo(%RootDir%
Pause
the directory where the batch file is located could be different from the current directory cmd.exe operates in.
TO get the batch file root path use:
for %%a in ("%0") do echo %%~da
To ger the current directory use
echo "%cd:~0,3%"
And let us not forget the &REM trick.
#ECHO OFF
set "root=%cd:\="&rem %
echo %root%

Why is a folder not deleted with RMDIR in batch file as last command?

I have the following code in a .bat file:
#echo off
xcopy /Y /S %CD%\Code\Release C:\Users\%USERNAME%\Desktop\ShareIt /I
cls
cd C:\Users\%USERNAME%\Desktop\ShareIt\
call "Trabalho AEDA.exe"
xcopy /Y /S C:\Users\%USERNAME%\Desktop\ShareIt\FICHEIROS\ %CD%\Code\Release\FICHEIROS\
RMDIR /S /Q C:\Users\%USERNAME%\Desktop\ShareIt
That copies a folder to a location, runs the .exe from it and then it overwrites the original files in my folder and has do delete the ones initially copied.
The folder I copy to the user desktop has other folder inside, and the .exe.
At the final line of the .bat, it deletes everything in the folder, but the folder is kept in the Desktop folder. I want to delete it, too. I tried several instructions, but without success.
EDIT: That was the issue, thanks guys.
ShareIt folder isn't deleted probably because you are in the folder.
So, adding cd .. before RMDIR /S /Q C:\Users\%USERNAME%\Desktop\ShareIt solves it.
The main problem with deleting ShareIt folder is already answered by the other answers.
It is not possible on Windows to delete a folder which is the current working directory of any running process or in which a file is opened by an application with a file lock preventing the deletion of the opened file.
In this case the ShareIt folder is the current working directory of the command process executing the batch file because the batch file explicitly sets this directory as working directory. The solution is making any other directory the working directory for the command process.
But this is not the only potential problem of the few command lines of this batch file. There are some more.
%CD% could expand to a path which contains perhaps one or more spaces or other characters like &()[]{}^=;!'+,`~ which require enclosing complete folder path in double quotes. This list of characters is output on running in a command prompt window cmd /? in last paragraph on last output help page.
Also %CD% expands to a folder path usually not ending with a backslash, except the current directory is the root directory of a drive. So %CD%\Code\Release could for example expand to C:\\Code\Release with two backslashes in path. However, this is no real problem as Windows kernel corrects the path automatically on whatever file system access function is used internally by xcopy for copying the files and folders.
Parameter /I lets xcopy interpret the target string as folder path even if folder path is not ending with a backslash, but only if multiple files are copied like when source path is a folder path like it is here obviously the case. Otherwise on copying just a single file /I is ignored by xcopy. Best for a folder path as target is specifying the target folder path with a backslash as then xcopy interprets target always as folder path. It is easier to use here paths relative to current directory and omit %CD% completely.
C:\Users\%USERNAME% is not good as the user's profile directory can be also on a different drive than C: and can be in a different directory than Users, for example on Windows XP. The path C:\Users\%USERNAME% is the default for the user's profile directory since Windows Vista. But every user has the freedom to change it also on Windows Vista and later Windows versions. There is the environment variable USERPROFILE defined by Windows which contains full path to active user's profile directory. See Wikipedia article about Windows Environment Variables for more details about predefined Windows environment variables.
And of course the user's profile directory path could contain one or more spaces or other characters which again require double quotes around complete folder path or file name, for example if the user account name contains a space character. So again double quotes should be used wherever %USERPROFILE% or %USERNAME% is used in a folder or file name with path.
Command CD without parameter /D changes the current directory only on current drive if the new current directory exists at all on current drive. So with current directory on starting the batch file being on a different drive than drive C:, changing the current directory with used code would not work at all.
The command CALL should not be necessary at all in case of Trabalho AEDA.exe is really an executable as the file extension indicates. CALL can be used also for an executable, but it is designed primary for running a subroutine or calling another batch file from within a batch file.
The last two lines do not really make sense because the current directory was changed by the batch file to ShareIt folder in desktop folder of current user. Therefore %CD%\Code\Release\FICHEIROS\ expands to
C:\Users\%USERNAME%\Desktop\ShareIt\Code\Release\FICHEIROS\
as target path for xcopy and the last line deletes the entire folder
C:\Users\%USERNAME%\Desktop\ShareIt
That is obviously not the intention. xcopy in last but one line should copy the files to a subdirectory in initial current directory.
I suggest to use this batch code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
%SystemRoot%\System32\xcopy.exe Code\Release "%USERPROFILE%\Desktop\ShareIt\" /C /Q /R /S /Y >nul
pushd "%USERPROFILE%\Desktop\ShareIt"
if not errorlevel 1 "Trabalho AEDA.exe" & popd
%SystemRoot%\System32\xcopy.exe "%USERPROFILE%\Desktop\ShareIt\FICHEIROS" Code\Release\FICHEIROS\ /C /Q /R /S /Y >nul
setlocal EnableDelayedExpansion
if "!CD:%USERPROFILE%\Desktop\ShareIt=!" == "!CD!" RMDIR /S /Q "!USERPROFILE!\Desktop\ShareIt"
endlocal
endlocal
The last IF conditions needs most likely an additional explanation for what is its purpose.
!CD:%USERPROFILE%\Desktop\ShareIt=! results in searching case-insensitive in current directory path string for all occurrences of the path string to ShareIt directory in desktop directory of current user and replacing all found occurrences by an empty string. So it depends on what is the current directory on what happens here.
In case of current directory is %USERPROFILE%\Desktop\ShareIt, the string left of comparison operator == becomes "". In case of current directory is a subdirectory of %USERPROFILE%\Desktop\ShareIt, the string left of == becomes a relative path to this directory enclosed in double quotes. In all other cases the string left of == is not modified at all and expands to path of current directory enclosed in double quotes like the string right of the comparison operator.
So the IF condition checks if the current directory is NOT the ShareIt directory or a subdirectory of this directory in the desktop directory of current user. Only in this case it is safe to delete the entire ShareIt directory.
Note: The IF condition does not work correct on %USERPRFOLE% expands to a folder path string containing one or more ! because of enabled delayed variable expansion.
For understanding all other 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 /?
cd /?
echo /?
endlocal /?
if /?
popd /?
pushd /?
set /?
setlocal /?
xcopy /?
There should be read also the Microsoft documentation about Using command redirection operators for an explanation of >nul.
Since you execute
cd C:\Users\%USERNAME%\Desktop\ShareIt\
It's the current directory when you execute the now delete the directory command.
Move to another directory, and then try the deletion

Batch script that do a backup

im a beginner in batch script,and im trying to do a script that ask for a file path,then create a folder(in the current folder)and I want the created folder name to be like "sav-DATE-TIME"(for example,right now the folder would be named: sav-2015-12-07-18-55-00) and then copy the file given by the user filepath in the created folder.So far I did this:
#ECHO OFF
SET /P pathh=Enter the path
SET foldname=sav%DATE%%TIME%
mkdir %foldname%
cd %foldname%
xcopy /s/e %pathh% %cd%
pause
However when I run this,no matter how I enter the path with " or ' or nothing around the path,it always says that the path is incorrect,and also it create a folder with random number(74,56,21...)as the folder name and I dont understand why it wont work properly,wich mean creating the folder with name as said at the begining of question and also saying the path is always incorrect.
thank you!
You need to use quotes when the variables are expanded (used). The first set of quotes below is only to ensure that you don't have any trailing white space chars. Try this:
#ECHO OFF
SET /P pathh=Enter the path
SET "foldname=sav-%DATE%-%TIME:~0,8%
SET "foldname=%foldname::=-%
echo foldname=%foldname%
rem mkdir "%foldname%"
rem cd "%foldname%"
rem xcopy /s/e "%pathh%" "%cd%"
pause
Remove the rem's when it looks good
set foldname=%date:~-4%-%date:~3,2%-%date:~0,2%:%time:~0,2%:%time:~3,2%,%time:~6,2% this should give you 2015-12-09-10:56,17
Then mkdir %foldname%

When is the CD environment variable updated?

The tipp to use the CD environment variable from batch scripts to get the current working directory is commonly posted. But CD won't get updated when calling another batch file. Then the cd command echoes the new path of the other batch file, but %CD% (or !CD!) is not updated. Example:
#echo off
cd %~dp0
echo in %0: CD=%CD%
pause
call X:\testcall.cmd
Save this as C:\testcall.cmd and X:\testcall.cmd, then run C:\testcall.cmd. You should see that the value of CD has not changed. This seems not to dependend on call; none of the following works:
start /D <NEW_DIR> <OTHER_CMD_FILE>
start cmd /c <NEW_DIR>\<OTHER_CMD_FILE>
cmd /c <NEW_DIR>\<OTHER_CMD_FILE>
<NEW_DIR>\<OTHER_CMD_FILE>
cd %~dp0
pushd %~dp0
CD will keep it's old value, while cd (the command) shows the correct directory. Therefore I set CD at the begin of a script:
set CD=%~dp0
...while assuming cmd.exe sets CD only if this variable is yet unset. True?
%CD% is the current directory, while %~dp0 is the directory of the currently-running script (with trailing '\').
Also, don't set an env. var called CD, since it will override the default %CD% pseudo-var, and will be incredibly confusing - see OldNewThing - ERRORLEVEL is not %ERRORLEVEL%.
For example, when running c:\temp\a.cmd, which is:
#echo off
echo Currently running script: %~dpnx0
cd %~dp0
echo scriptDir=%~dp0, CD=%CD%
cd %~dp0a
echo scriptDir=%~dp0, CD=%CD%
set CD=bogus value
echo scriptDir=%~dp0, CD=%CD%
output:
Currently running script: c:\temp\a.cmd
scriptDir=c:\temp\, CD=c:\temp
scriptDir=c:\temp\, CD=c:\temp\a
scriptDir=c:\temp\, CD=bogus value
Diagnosis
You have at some point set the CD variable explicitly. If you do this it will no longer automatically reflect the current working directory. To undo this, set it to empty:
set CD=
It will then begin working again.
Why is this? Well, the automatic CD variable was introduced as a feature. I assume they just didn't want to break pre-existing scripts which already used that varible name. So if you set it explicitly, CMD will assume you are doing so on purpose.
Discussion
Firstly, if the parent process has an explicitly set CD variable, it will be inherited by the child processes.
On the other hand, you shouldn't expect any of these to update the value of %CD% for the parent process:
start /D <NEW_DIR> <OTHER_CMD_FILE>
start cmd /c <NEW_DIR>\<OTHER_CMD_FILE>
cmd /c <NEW_DIR>\<OTHER_CMD_FILE>
These all create new processes, the new process then changes its own working directory. You should not expect this to affect the parent process.
The final one, does not update the working directory at all, unless OTHER_CMD_FILE executes a CD command:
<NEW_DIR>\<OTHER_CMD_FILE>
Just because you executed a script in a different directory does not mean that the script's working directory will change. The script working directory does not have to be set to the location of the script.
Advice
Relying on the working directory being set to anything in particular is generally a bad idea.
You probably want something like this:
SET SCRIPT_DIR=%~dp0
Then use (for example) "%SCRIPT_DIR%\config.txt" to refer to a file in that directory.
Alternatively if you wish to rely on the current directory, use cd /d %~dp0
I build two batch files from your comment
test1.bat - located in C:\temp
#echo off
cd %~dp0
echo File %~f CD=%CD%
call X:\test2.bat
test2.bat - located in *X:*
#echo off
cd %~dp0
echo File %~f CD=%CD%
Starting test1 it from C:\temp, the output is
File C:\temp\test1.bat CD=C:\temp
File X:\test2.bat CD=C:\temp
The result is absolutly correct!
Starting a batch file (or any other program) with an absolute or relative path doesn't change the current directory.
The CD seems to fail, as CD can't change the drive the way you used it.
You need to add a switch CD /d %~dp0
You can set the %cd% variable to whatever you want, the real current directory for the C: drive is stored in the %=c:% variable, and you can't change this with the set command:
#echo off
echo Currently running script: %~dpnx0
cd %~dp0
echo scriptDir=%~dp0, CD=%CD%
set CD=bogus value
echo scriptDir=%~dp0, CD=%CD%, =c:=%=c:%
set =c:=bogus value
echo scriptDir=%~dp0, CD=%CD%, =c:=%=c:%
Output is:
Currently running script: C:\OldDir\a.bat
scriptDir=C:\OldDir\, CD=bogus value
scriptDir=C:\OldDir\, CD=bogus value, =c:=C:\OldDir
syntax error.
scriptDir=C:\OldDir\, CD=bogus value, =c:=C:\OldDir
The =C: variable for a child process is always set from the parent process. If you avoid the setlocal command in your script OR choose endlocal, you can change persistend the current directory for the current cmd session:
C:\OldDir>type script.bat
cd c:\newdir
C:\OldDir>script
C:\OldDir>cd c:\newdir
C:\NewDir>
.
C:\OldDir>type script.bat
setlocal
cd c:\newdir
C:\OldDir>script
C:\OldDir>setlocal
C:\OldDir>cd c:\newdir
C:\OldDir>
Using the script-directory %~dp0 can be a solution, but usually isn't. This works better:
cd >tmpfile
set /P CD= <tmpfile
del tmpfile
This solution is consistent with the CD variable. CD contains the current directory, not ending in a slash character if it is not in the root directory of the current drive. The path printed by cd behaves exactly so.
I'm using this for years and had no problems setting CD explicitly. Later I began using PWD (as in Bash). This variable is not reserved in MSDOS. So my question here was actually: "Can we get rid of these lines, or is this some MSDOS idiom?"
Some have written that setting CD explicitly is bad. Why? MSODS was never properly designed, and is not further developed. Anything you can do with it is legal. There is no bad MSDOS programming - just good and bad hacks. I know no other language where this perspective is legal to such an extent.

How can I change the root path in a .bat file?

In a .bat file, How can I change the root path to be c:\temp\code
cd /d c:\temp\code
I'd suggest pushd over cd in this case. That way you can restore the previous directory with popd at the end. Unless a batch file should actually change the path even after it has been run, I'd always restore it at the end of the batch:
#echo off
rem change current directory
pushd C:\Temp\Code
rem ...
rem something your batch needs to do
rem ...
rem restore old working directory
popd
Sometimes, doing cd c:\temp\code only doesn't work if you're in another drive. This way works all the time:
c:
cd c:\temp\code

Resources