User to user discussion and support for UltraEdit, UEStudio, UltraCompare, and other IDM applications.

Help with setting up and configuring custom user tools in UltraEdit (based on command line input)
7 posts Page 1 of 1
I'm trying to make a user tool in UltraEdit to copy the the active file from its current path to another path. (No desire to operate like Save As, as I need to keep the active file open from its current path.)

Here's an example:

Active file path: "c:\parent_folder\super\code\file.txt"
To be copied to overwrite: "d:\super\code\file.txt"

Could I have some assistance with the command for this?

Also, would it be much more difficult to have another tool to do the above operation over all files open?

Here's an example:

Active file path: "c:\parent_folder\super\code\file.txt"
To be copied to overwrite: "d:\super\code\file.txt"

and

2nd active file path: "c:\parent_folder\related\stuff\file2.txt"
To be copied to overwrite: "d:\related\stuff\file2.txt"

Thank you!
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 help of the commands xcopy and set got 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
set "SourceFile=%1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
xcopy %SourceFile% %TargetFile% /H /K /R /Y

Most interesting here is the third line where a copy of full source file name (with 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. But there is no such switch for telling xcopy that the target is a file.

So I ran a WWW search and found the page xcopy file, rename, suppress "Does xxx specify a file name..." message where Arnshea posted a good solution 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
set "SourceFile=%1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
echo F | xcopy %SourceFile% %TargetFile% /H /K /R /Y

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
set "SourceFile=%1"
set "TargetFile=%SourceFile:C:\parent_folder=D:%"
if exist %TargetFile% (
   copy /Y %SourceFile% %TargetFile%
) else (
   echo F | xcopy %SourceFile% %TargetFile% /H /K /R /Y
)

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
if "%~1" == "" goto :EOF
setlocal
if /I "%~1" == "/L" goto ListFile

: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%
goto :EOF

: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
goto :EOF

:ListFile
for /F "usebackq delims=" %%F in ("C:\Temp\ListFile.tmp") do call :CopyFile "%%~F"
del "C:\Temp\ListFile.tmp"
endlocal


Explanation for the batch file:

With the first command echo off printing all executed commands to stdout (console window) is disabled. To hide execution of this command the @ is added left to the command.

The second 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 third line results in creating a local copy of entire environment variables table which is destroyed and previous table restored either by command endlocal (multiple files from list file) or when command processor reaches end of batch processing (single file on goto :EOF in CopyFile or MakeDir block).

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 8 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 as well as an error message output by command copy to stderr with 2>nul. You can remove these 2 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.

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 for which can be read by executing for /?

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 16 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.

For copying multiple files as specified in a list file containing line by line the names of the files to copy with full path, a for loop is used to read one file name after the other from the list file with fixed name and the commands explained above are executed with 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. Command processor knows itself if goto :EOF results in just exiting subroutine and returning to for loop or really exiting 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.
For copying active file using the batch file CopyFileOrFileList.bat configure a user tool with following settings:

On tab Command:

Menu item name: Copy Active File (for example)
Command line: "full path to\CopyFileOrFileList.bat" "%f"
Working directory: (let it empty)
Toolbar bitmap/icon: (let it empty or browse to a BMP or ICO file with a suitable copy icon)

On tab Options:

Program Type: DOS Program
Save active file: checked
Save all files first: unchecked

On tab Output:

Command Output: Output to list box
Show DOS Box: unchecked
Capture output: unchecked
Replace selected text with: No replace


On execution of the tool the active file is saved if not already saved and then the full name of the active file is passed in double quotes to the batch file. On success nothing is printed to stdout and stderr. Therefore UltraEdit could not capture anything even if the option Capture output would be checked.

In UE v18.20.0.1020 the output window is opened always on start of the tool if the capturing option is enabled. In older versions of UltraEdit the behavior was in my point of view better because the output window was opened after tool finished and only if something could be captured by UltraEdit. So with an older version of UltraEdit it would make sense to check the option Capture output to see for the rare case that copying the file failed (drive or permission problem) the error message(s) printed to stderr and captured by UltraEdit in the output window which is opened automatically by UltraEdit in this case.


The user tool for copying all opened files except new file(s) not yet stored requires following settings:

On tab Command:

Menu item name: Copy Opened Files (for example)
Command line: "full path to\CopyFileOrFileList.bat" /L
Working directory: (let it empty)
Toolbar bitmap/icon: (let it empty or browse to a BMP or ICO file with a suitable copy icon)

On tab Options:

Program Type: DOS Program
Save active file: unchecked
Save all files first: unchecked

On tab Output:

Command Output: Output to list box
Show DOS Box: unchecked
Capture output: checked
Replace selected text with: No replace


But this second tool is not enough to get all opened files with a file name copied. An additional UltraEdit script is necessary to create the list file C:\Temp\ListFile.tmp with the names of all open files.

Code: Select all
var sFileNames = "";
for (var nDocIndex = 0; nDocIndex < UltraEdit.document.length; nDocIndex++ ) {
   if (UltraEdit.document[nDocIndex].path.indexOf("\\") >= 0) {
      sFileNames += UltraEdit.document[nDocIndex].path+"\r\n";
      UltraEdit.document[nDocIndex].setActive();
      UltraEdit.save();
   }
}

if (sFileNames.length > 0) {
   UltraEdit.newFile();
   UltraEdit.activeDocument.unicodeToASCII();
   UltraEdit.activeDocument.unixMacToDos();
   UltraEdit.activeDocument.write(sFileNames);
   UltraEdit.saveAs("C:\\Temp\\ListFile.tmp");
   UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
   // UltraEdit.outputWindow.showStatus=false;
   UltraEdit.runTool("Copy Opened Files");
}


It is up to you if you want to remove the 2 rem in the batch file to get success messages on copying the file(s) captured to output window if user tool option Capture output is checked for both user tools. For copying all opened files you need to turn off writing script status to the output window with the currently commented command near end of the small script.
Mofi, I'm overly impressed with your work and helpfulness in your post! Thank you!
Hi Mofi,

I use your script and it works perfectly.
One little question. For me, it would be better when the script CopyFileOrFileList.bat saves only the files, that I have actually changed.
In line 5-6 you save every file, even if it is not necessary. Sometimes I've open some files only for reading.

Code: Select all
UltraEdit.document[nDocIndex].setActive();
UltraEdit.save();

Background: My source location is monitored by a Version Control System (VCS). Even something is saved, the VCS mark it as changed. But sometimes there is nothing changed.

Do you know what I mean?

Thank you.
Hi Mario!

I could not believe that usage of UltraEdit.save() results in saving even unmodified files. But you are right, this command saves even unmodified files. I have just reported this in my opinion wrong behavior by email to IDM support.

The UltraEdit.document class does not have a property or a function to determine the modification status. So it is impossible to find out from within a script if active file is a modified or unmodified file. It is only possible to determine read-only status.

Instead of using UltraEdit.save() on each named file, it is possible to call on first named file the command UltraEdit.saveAll() which saves only modified files.

Code: Select all
var sFileNames = "";
for (var nDocIndex = 0; nDocIndex < UltraEdit.document.length; nDocIndex++ ) {
   if (UltraEdit.document[nDocIndex].path.indexOf("\\") >= 0) {
      if (!sFileNames.length) UltraEdit.saveAll();
      sFileNames += UltraEdit.document[nDocIndex].path+"\r\n";
   }
}

if (sFileNames.length > 0) {
   UltraEdit.newFile();
   UltraEdit.activeDocument.unicodeToASCII();
   UltraEdit.activeDocument.unixMacToDos();
   UltraEdit.activeDocument.write(sFileNames);
   UltraEdit.saveAs("C:\\Temp\\ListFile.tmp");
   UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
   // UltraEdit.outputWindow.showStatus=false;
   UltraEdit.runTool("Copy Opened Files");
}

But this command has a disadvantage in comparison to calling UltraEdit.save() on each named file: the Save As dialog is opened for every new and modified file on using UltraEdit.saveAll(). So if there are 1 or more "text buffers" opened additionally to the named files, using UltraEdit.saveAll() requires pressing button Cancel in Save As one or more times if those new, modified files should not be saved now. New files with no modification are ignored by UltraEdit.saveAll().

However, this small variant of my initial script could be nevertheless better for you than the script which activates each named file and save it even if nothing changed since opening the file or last saving.
Best regards from Austria
Hi Mofi,

that works perfect and faster, because they don't need to focus each file. I inspect your disadvantage with every new and modified file, but in my case thats OK.

Thanks a lot.

The final touch would be, to set scripts as button in the toolbar, but I read, that's not possible. So I use the hotkey.

Regards,

Mario
7 posts Page 1 of 1
cron