Create '.bak' Backups for Selected Files

  • Button that creates '.bak' backups for any number of selected files.
  • If a .bak already exists for a file, it will create .bak2, .bak3 and so on.
  • Works even if each selected file has a different number of .baks already
  • Allows changing .bak extension to anything (See 'backupExtension' variable)

Example Result:
image

// Button that creates '.bak' backups for any number of selected files. If a .bak already exists for a file, it will create .bak2, .bak3 and so on.
// By ThioJoe
// Updated: 7/26/22 (First Version)
// https://resource.dopus.com/t/create-bak-backups-for-selected-files/41808

function OnClick(clickData) {
	// You can change the backup extension base string to be whatever you want here. Must include period.
	// Default = '.bak'
	backupExtension = '.bak'
	////////////////////////////////////////////////////////////////////////

	function createBak(fileItem) {
		lastBakNum = 0;
		// Create item object of selected file
		selectedFile = fileItem;
		//Get name of selected file
		selectedFileFullName = String(selectedFile.name);
		selectedFileExt = String(selectedFile.ext);
		selectedFileNameStem = String(selectedFile.name_stem);
		
		//Go through filenames in folder, if any contains fileFullName.bak, check if # at end is larger than current, if so record into lastBakNum
		enumFiles.moveFirst();
		while (enumFiles.atEnd() == false) {
			currentFileName = String(enumFiles.item().name)
			currentFileNameExt = String(enumFiles.item().ext)
				
			// Checks if stem of currently scanned file is same as selected file with .bak added
			theoreticalBakName = selectedFileFullName + backupExtension;
			theoreticalBakNameLength = theoreticalBakName.length;
			
			//Checking if the currently scanned file is already a .bak file of the selected file
			//By checking if scanned file contains selected file name + bak, from beginning
			if (currentFileName.substr(0, theoreticalBakNameLength) == theoreticalBakName) {
				//Checks if extension is .bak or .bak*
				if (currentFileNameExt.substr(0,backupExtensionLength) == backupExtension) {
					// If existing backup file extension is exactly .bak with nothing after, set lastBakNum to 1, so next one will be .bak2, not .bak1 (.bak1 could be  with .bak)
					if (currentFileNameExt == backupExtension) {
						if (lastBakNum == 0) {
							lastBakNum = 1;
						}
					} 
					// If it starts with .bak but has something after .bak
					else {
						// Gets text or number after ".bak", which should be a number
						extEnding = currentFileNameExt.substr(backupExtensionLength);
						//Checks if anything after .bak is not a number
						if (isNaN(extEnding) == false) {
							// Parse the ending number into an integer in base 10
							extEndingNum = parseInt(extEnding, 10);
							// Only update lastBakNum if it is the largest .bak # found so far
							if (extEndingNum > lastBakNum) {
								lastBakNum = extEndingNum;
							}
						}
					}
				}
			}
			enumFiles.moveNext();
		}
		
		// If there is no already existing .bak or .bak# of the selected file, create them
		if (lastBakNum == 0) {
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension + ' HERE'
			clickData.func.command.RunCommand(commandString);
		}
		else {
			newBakNum = lastBakNum + 1;
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension + newBakNum + ' HERE';
			clickData.func.command.RunCommand(commandString);
		}
	}
	
	// Get data about selected files, and rest of the files in the folder
	allSelectedFiles = clickData.func.sourcetab.selected_files;
	enumSelectedFiles = new Enumerator(allSelectedFiles);
	enumFiles = new Enumerator(clickData.func.sourcetab.files);  //Enumerate all files in folder. Does this before any bak files are created to save unecessary checking later
	backupExtensionLength = backupExtension.length;

	// Runs the main function for each selected file
	enumSelectedFiles.moveFirst();
	while (enumSelectedFiles.atEnd() == false) {
		currentFile = enumSelectedFiles.item()
		createBak(currentFile);
		enumSelectedFiles.moveNext();
	}
}
8 Likes

The .bak file should be in the same folder as the original file is.

If you are okay with the counter being added to the filename, you could use

@nodeselect
Copy DUPLICATE AS=*.bak WHENEXISTS=rename
2 Likes

The .bak file should be in the same folder as the original file is.

It should already work like that, do you mean the files went somewhere else for you?

It's best to avoid HERE in scripts.

Instead, use TO in the Copy command line or set the destination in the command object via SetDest before running the line.

https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Scripting/Command.htm

In this specific case, you can also use the DUPLICATE argument.

https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Copy.htm

is this code supposed to works even with folders? after a first copy it stops (it create a 'copy of this' but not a 'copy (1) of this')

It will create one bak folder and then keep adding duplicates to that folder - probably not what you want.

To duplicate folders, you could use:

@nodeselect
Copy DUPLICATE AS=*-{date|yyyyMMdd}-{time|HHmmss}

or, if you prefer a counter:

@nodeselect
@nofilenamequoting
@dirsonly
@firstfileonly
FileType NEW=directory NEWNAME="norename:{file}"
Copy DUPLICATE AS="{$newfile}"

The second button will only work for one folder at a time!

4 Likes

great!!, the first solution is simply perfect for my needs, many thanks

I love the .bak script. But I'd like to see it modified so that .bak* files get created in or moved to a designated backup folder so that you don't have to move them after the fact.

If you want to add the date and time to the backup filename, (e.g., MyFile.xlsm_10-10-2022_06-02-53.bak), add {date|MM-dd-yyyy}_{time|HH-mm-ss}. Change this:

if (lastBakNum == 0) {
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension + ' HERE'
			clickData.func.command.RunCommand(commandString);
		}
		else {
			newBakNum = lastBakNum + 1;
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension + newBakNum + ' HERE';
			clickData.func.command.RunCommand(commandString);
		}

to this:

		if (lastBakNum == 0) {
			commandString = 'Copy DUPLICATE "' + selectedFile + '" AS *_{date|MM-dd-yyyy}_{time|HH-mm-ss}' + backupExtension + ' HERE';
			clickData.func.command.RunCommand(commandString);
		}
		else {
			newBakNum = lastBakNum + 1;
			commandString = 'Copy DUPLICATE "' + selectedFile + '" AS *_{date|MM-dd-yyyy}_{time|HH-mm-ss}' + backupExtension + newBakNum + ' HERE';
			clickData.func.command.RunCommand(commandString);
		}
2 Likes

I tried the script, but the backup file is created in the destination pane. Should this not be avoided by the "here" argument?
(Is that the reason to avoid "here" in scripts?)

Try what lxp wrote above:

Yes, then it works

		// If there is no already existing .bak or .bak# of the selected file, create them
		if (lastBakNum == 0) {
			clickData.func.command.SetDestTab(clickData.func.sourcetab);
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension;
			clickData.func.command.RunCommand(commandString);
		}
		else {
			newBakNum = lastBakNum + 1;
			clickData.func.command.SetDestTab(clickData.func.sourcetab);
			commandString = 'Copy "' + selectedFile + '" AS *' + backupExtension + newBakNum;
			clickData.func.command.RunCommand(commandString);
		}

But according to this topic "Copy MOVE HERE CREATEFOLDER" doesn't create folder in source? - #9 by Leo the here argument should work, right?

What do you mean by "the backup file is created in the destination pane?" Using HERE, The backup file should be created in the directory that the file you're backing up resides. Where is your backup being created?

I use dual display mode. It was created "on the other side". When I used the script for a file on the left side the bak was created on the right side and vice versa (in the current visible tab).

Ok, I deleted the word HERE from my script, and the backup still gets created, and in the correct location, with a dual window open. This is the script without the HERE:

commandString = 'Copy DUPLICATE "' + selectedFile + '" AS *{date|MM-dd-yyyy}{time|HH-mm-ss}' + backupExtension;

So, I guess you don't even need the HERE, and your problem is something else.

I see your script doesn't have DUPLICATE in it. Try making it Copy DUPLICATE.

Yes - in the version without DUPLICATE the "here" has no function (any longer). But you have to define the sourcetab as destination tab like in my code snippet above.

Or you have to use "Copy DUPLICATE" - then no "HERE" or "setDestTab" is necessary.

But is it intended, that the "HERE" has no function any more? I assume it worked in the past.

EDIT: If the selected item is a folder nothing happens.