tool to copy active file to another path

tool to copy active file to another path

10
Basic UserBasic User
10

    Nov 08, 2012#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!

    6,686585
    Grand MasterGrand Master
    6,686585

      Nov 10, 2012#2

      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.

        Nov 10, 2012#3

        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.

        10
        Basic UserBasic User
        10

          Nov 11, 2012#4

          Mofi, I'm overly impressed with your work and helpfulness in your post! Thank you!

          231
          Basic UserBasic User
          231

            Jul 14, 2015#5

            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.

            6,686585
            Grand MasterGrand Master
            6,686585

              Jul 14, 2015#6

              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 an UC/UE/UES for Windows user from Austria

              231
              Basic UserBasic User
              231

                Jul 15, 2015#7

                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

                3

                  Aug 17, 2021#8

                  I found this thread today and found the solution to be great and simple to implement!

                  I was trying to customize it a little further by passing the current directory to the batch file as %2 with "%p" in the command line and then use this location as a variable in the existing code as mentioned to change in line 9 and line 18. This is done so I don't have to edit the batch file with a location on different projects, I can just run it and it will send the active file across.

                  See my modifications below. The output looks okay in the echo commands I've set at the start to try and debug but ultimately the script fails.
                  Could anyone point out what I'm missing?

                  Code: Select all

                  @echo off
                  setlocal EnableExtensions DisableDelayedExpansion
                  if "%~1" == "" goto :EOF
                  if /I "%~1" == "/L" goto ListFile
                  
                  :CopyFile
                  set "SourceFile=%~1"
                  set "SourceDir=%~2"
                  set "TargetDir=E:\Transfer"
                  echo Copying
                  echo "%SourceFile%"
                  echo From 
                  echo "%SourceDir%"
                  echo to 
                  echo "%TargetDir%"
                  
                  pause
                  
                  set "TargetFile=%SourceFile:%SourceDir%=%TargetDir%%"
                  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:%SourceDir%=%TargetDir%%"
                  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 eol0| delims=" %%I in ("C:\Temp\ListFile.tmp") do call :CopyFile "%%~I"
                  del "C:\Temp\ListFile.tmp"
                  endlocal

                  6,686585
                  Grand MasterGrand Master
                  6,686585

                    Aug 17, 2021#9

                    The following command line does not work.

                    Code: Select all

                    set "TargetFile=%SourceFile:%SourceDir%=%TargetDir%%"
                    The Windows command processor cmd.exe does not support immediate environment variable expansions inside an immediate environment variable substitution. The Windows command processor cannot know that it has to expand in a first processing step the environment variable references %SourceDir% for the search string and %TargetDir% for the replace string and in a second processing step the environment variable substitution on the string value assigned to the environment variable SourceFile by searching case-insensitive with the search string left the equal sign and replacing all found occurrences by the replace string right to the equal sign.

                    That is only possible by using delayed environment variable expansion with cause exactly these two step processing of the command line. So needed would be:

                    Code: Select all

                    setlocal EnableDelayedExpansion
                    set "TargetFile=!SourceFile:%SourceDir%=%TargetDir%!"
                    endlocal & set "TargetFile=%TargetFile%"
                    However, it looks like the entire source file path should be replaced by E:\Transfer because of %f in the tool configuration passes the full qualified file name of the active file to the batch file as first argument and %p passes the full path of the active file as second argument to the batch file. "%p" as second argument string for the batch file is not needed at all in the user/project tool configuration in this use case by using the following batch code:

                    Code: Select all

                    @echo off
                    setlocal EnableExtensions DisableDelayedExpansion
                    if "%~1" == "" goto :EOF
                    if /I "%~1" == "/L" goto ListFile
                    
                    :CopyFile
                    set "SourceFile=%~1"
                    set "TargetPath=E:\Transfer"
                    set "TargetFile=%TargetPath%\%~nx1"
                    copy /Y "%SourceFile%" "%TargetFile%" >nul 2>nul
                    if errorlevel 1 goto MakeDir
                    rem echo Copied "%SourceFile%"
                    goto :EOF
                    
                    :MakeDir
                    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 eol=| delims=" %%I in ("C:\Temp\ListFile.tmp") do call :CopyFile "%%~I"
                    del "C:\Temp\ListFile.tmp"
                    endlocal
                    
                    But let me suggest another solution. The batch file is executed by a project tool with "%f" "%rp" which passes to the batch file the full qualified file name of the active file as first argument string and the full path of the .prj file of the opened project as second argument string. Further let´s assume that the .prj project file of the opened project is in the root folder of the project. Then the following batch file can be used to copy the active project file with replicating the directory structure below the root directory of the project.

                    Code: Select all

                    @echo off
                    setlocal EnableExtensions DisableDelayedExpansion
                    if "%~1" == "" goto :EOF
                    if /I "%~1" == "/L" goto ListFile
                    
                    :CopyFile
                    set "SourceFile=%~1"
                    set "TargetPath=E:\Transfer\"
                    if "%~2" == "" goto DoCopy
                    set "SourcePath=%~dp1"
                    set "ProjectPath=%~2"
                    if not "%ProjectPath:~-1%" == "\" set "ProjectPath=%ProjectPath%\"
                    setlocal EnableDelayedExpansion
                    set "VariableTargetPath=!SourcePath:%ProjectPath%=%TargetPath%!"
                    if "!VariableTargetPath!" == "!SourcePath!" set "VariableTargetPath=!TargetPath!"
                    endlocal & set "TargetPath=%VariableTargetPath%"
                    :DoCopy
                    set "TargetFile=%TargetPath%%~nx1"
                    copy /Y "%SourceFile%" "%TargetFile%" >nul 2>nul
                    if errorlevel 1 goto MakeDir
                    rem echo Copied "%SourceFile%" to "%TargetFile%"
                    goto :EOF
                    
                    :MakeDir
                    mkdir "%TargetPath%"
                    if not errorlevel 1 copy /Y "%SourceFile%" "%TargetFile%" >nul
                    if errorlevel 1 goto NoSuccess
                    rem echo Copied "%SourceFile%" to "%TargetFile%"
                    :NoSuccess
                    goto :EOF
                    
                    :ListFile
                    for /F "usebackq eol=| delims=" %%I in ("C:\Temp\ListFile.tmp") do call :CopyFile "%%~I"
                    del "C:\Temp\ListFile.tmp"
                    endlocal
                    
                    The batch file is written to be executed also without the project file path as second argument in which case the active file is copied to the target folder as defined in the batch file. There is also code added to copy the active file to the target folder as defined in the batch file if the delayed environment variable substitution fails to replace from source file path the project file path by the defined target folder path, i.e. there is a project opened in UltraEdit and the project tool is used to copy the active file, but the active file is not part of the project and has therefore a path not beginning with the project file path.

                    Please note that %~dp1 expands to full path of source file always ending with a backslash. UltraEdit passes the full path of the project file also with a backslash at end to the batch file and if that should not be done in future by UltraEdit, the batch file appends a backslash to project path itself. For that reason the target folder path as defined in the batch file must also end with a backslash or the full path of the target file would be built wrong. As the target folder path ends always with a backslash now, the concatenation of the target folder path with the name of the source file is done without an additional backslash in comparison to first batch file code which is designed for a target folder path not  ending with a backslash.
                    Best regards from an UC/UE/UES for Windows user from Austria

                    3

                      Aug 19, 2021#10

                      Thanks Mofi, worked like a charm!