Okay, I think I got it. This was harder to develop as I first thought simply because of Microsoft forgot a switch for command
xcopy.
I used the help of the commands
xcopy and
set output by running
xcopy /? and
set /? for writing the first batch file for copying active file to the other location with replacing drive and first directory in path for the target file name.
Code: Select all
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFile=%~1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
%SystemRoot%\System32\xcopy.exe "%SourceFile%" "%TargetFile%" /H /K /R /Y
endlocal
Most interesting here is the fourth line where a copy of full source file name (without the perhaps surrounding double quotes) is created whereby the string
C:\parent_folder is replaced by
D: to get the full destination file name.
But there was a problem with this small batch file. While command
copy can be used only if the target directory tree exists, the command
xcopy can be used also if the target directory tree does not exist as
xcopy can create it. But
xcopy asks the user if the target is a file or a directory if the target directory does not exist. Command
xcopy has the switch
/I to tell the command that the target is a directory on copying multiple files and destination does not already end with a backslash. But there is no such switch for telling
xcopy that the target is a file.
I ran a world wide web search and found the page
xcopy file, rename, suppress "Does xxx specify a file name..." message where a good solution is posted for this problem. Putting
echo F| left to the command
xcopy results in answering the question always with character
F for
file.
Code: Select all
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFile=%~1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
echo F| %SystemRoot%\System32\xcopy.exe "%SourceFile%" "%TargetFile%" /H /K /R /Y
endlocal
But I was not happy with that as if the target file already exists, it is useless to pipe to command
xcopy the character
F. So I changed the batch file once more to this:
Code: Select all
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFile=%~1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
if exist "%TargetFile%" (
copy /Y "%SourceFile%" "%TargetFile%"
) else (
echo F| %SystemRoot%\System32\xcopy.exe "%SourceFile%" "%TargetFile%" /H /K /R /Y
)
endlocal
That's better, but I was still not happy with this solution. I don't like batch files which depend on language of the operating system.
The character
F is only correct for an English Windows. The command
xcopy on a German Windows XP requires
D for
Datei and
V for
Verzeichnis instead of
F for
File and
D for
Directory as on an English Windows.
So I thought it is better to simply copy the file and if this fails because the target directory tree does not exist, use command
mkdir to create the directory tree and copy the file once again.
Here is the final batch file which I named
CopyFileOrFileList.bat for both of your purposes: copying a single file and copying a list of files.
Code: Select all
@echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" goto EndBatch
if /I "%~1" == "/L" goto ListFile
set "SingleFile=yes"
:CopyFile
set "SourceFile=%~1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
copy /Y "%SourceFile%" "%TargetFile%" >nul 2>nul
if errorlevel 1 goto MakeDir
rem echo Copied "%SourceFile%"
if "%SingleFile%" == "no" goto :EOF
goto EndBatch
:MakeDir
set "SourcePath=%~dp1"
set "TargetPath=%SourcePath:C:\parent_folder=D:%"
mkdir "%TargetPath%"
if not errorlevel 1 copy /Y "%SourceFile%" "%TargetFile%" >nul
if errorlevel 1 goto NoSuccess
rem echo Copied "%SourceFile%"
:NoSuccess
if "%SingleFile%" == "no" goto :EOF
goto EndBatch
:ListFile
set "SingleFile=no"
for /F "usebackq eol=| delims=" %%I in ("C:\Temp\ListFile.tmp") do call :CopyFile "%%I"
del "C:\Temp\ListFile.tmp"
:EndBatch
endlocal
Explanation for the batch file:
Printing all executed commands to stdout (console window) is disabled with the first command
echo off. The character
@ is added left to the command to prevent an output of this command too.
The second line defines the required execution environment with enabled command extensions and disabled delayed variable expansion. This line results also in creating a local copy of the entire environment variables list which is destroyed and previous list restored by the command
endlocal at end of the batch file. There is enabled explicitly the command extensions required by rest of the batch file code although enabled by Windows default and disabled delayed variable expansion not needed and wanted here although disabled by Windows default. It is better not depending on Windows defaults and define explicitly the required execution environment in the batch file itself.
The third line is just for security in case of running this batch file by mistake without a parameter, for example when double clicking on this batch file.
The fourth line makes the decision on first parameter passed to the batch file if it should process a list of files or just a single file passed with full name to the batch file. With
/L as first parameter the batch file processes a list of files. For details on command
if execute in a command prompt window or via
Advanced - DOS Command in UltraEdit the command
if /?
Next target file name is created from source file name as explained already at beginning of this post.
ATTENTION: You need to modify the strings C:\parent_folder and D: in line 9 according to your paths.
Next the simple
copy command is executed with not really necessary switch
/Y (automatically set by default on using copy from within a batch file in comparison to using copy command outside of a batch file). The standard output of command
copy written to
stdout is redirected with
>nul to no-man's-land (device NUL) as well as an error message output by command
copy to
stderr with
2>nul. You can remove these two redirections if you want ever see what this first copy command does.
If command
copy fails to copy the file for example because of not existing target directory tree, it terminates with a value greater than 0. In this case the batch file continues on block starting with label
MakeDir. Otherwise the file was copied and the batch file processing ends with restoring previous environment or with returning to the
for loop processing the list file.
If copying the file failed, first the source path must be extracted from the full file name in double quotes. This can be done with
%~dp1 as explained in help of command
call which can be read by executing
call /?
Please note that
%~dp1 returns the drive letter with colon and the directory path ending with a backslash always without double quotes even if the full source name was surrounded by double quotes because of a possible space character in full name. This is important to know for usage of the target path as surrounding double quotes must be added again.
I think, how building the target path from source path doesn't need to be explained a second time.
ATTENTION: Don't forget to change the strings C:\parent_folder and D: on line 18 also according to your paths.
Now the command
mkdir can be used to create the target directory tree. Although this should never fail, it could nevertheless happen that making the directory tree fails (target drive does not exist, permission problem) and therefore there is one more condition based on
errorlevel returned by
mkdir to continue or exit the batch execution.
Next the
copy command is used a second time for copying the file to the other directory. It is on purpose that now
2>nul is not used anymore to redirect an error message to no-man's-land. If
copy command fails a second time to copy the file, the error message of the command should be printed to
stderr.
There is used a
for loop for copying multiple files as specified in a list file containing line by line the names of the files to copy with full path. The command
for reads one file name after the other from the list file with fixed name and the commands explained above are executed for each file name.
The string
usebackq could be removed when removing the double quotes around list file name
C:\Temp\ListFile.tmp which would be possible here as this file name does not contain a space character. But I don't know which file name and path you want to use for the list file.
ATTENTION: You have to adapt list file name C:\Temp\ListFile.tmp twice in the batch file according to your environment.
This loop looks very simple, but is very tricky. The command line processor
cmd replaces by default environment variables already on reading a line instead of accessing their values on execution. The help for command
set describes with two examples where this behavior is a problem. Using environment variables with varying values in a
for loop is one of those cases.
The solution used here is to
call the block starting with
CopyFile and ending above
ListFile like another batch file. This block can be imagined as a pseudo batch file within a batch file or as a batch subroutine. The Windows command processor knows itself if
goto :EOF results in just exiting subroutine and returning to
for loop or really exiting the batch processing.
Okay, that's the batch file. In next post I describe how to make use of it within UltraEdit for copying active file and all open files.