Hierarchical backup wanted.
-
To my knowledge, there’s no plugin to do such a thing.
But, with the PythonScript plugin (or another scripting plugin) you can do pretty much anything you want. You would set up a “notification” on the save event and then fire off some script code to make a copy of your file.
If you’d be willing to use the PythonScript plugin, I’ll volunteer to put together a demo for you (it’s really not difficult).
-
So it could be as simple as a script like what follows.
Note that this script only considers files that are mapped to a local C: through Z: drive.
Other (network) paths are possible, but for the point of illustration, that’s just an unnecessary complication.Also note that much more error checking and results reporting (presumably upon failure) could be done.
Typically the lines of the script would be added to the user’s startup.py file, so that it is transparently activated and sitting there waiting for user saves to happen, after Notepad++ initializes.
Anyway, the script:
# -*- coding: utf-8 -*- from Npp import notepad, NOTIFICATION import os import shutil import re def callback_notepad_FILESAVED(args): curr_buffer_id = notepad.getCurrentBufferID() notepad.activateBufferID(args['bufferID']) p = notepad.getCurrentFilename() m = re.match('([C-Z]):', p, re.I) if m: top_level_backup_dir = r'c:\temp\mybackups' backup_path = top_level_backup_dir + os.sep + 'Drive-{}'.format(m.group(1)) + p[2:] backup_dir = backup_path.rsplit(os.sep, 1)[0] if not os.path.isdir(backup_dir): os.makedirs(backup_dir) if os.path.isdir(backup_dir): shutil.copy2(p, backup_path) notepad.callback(callback_notepad_FILESAVED, [NOTIFICATION.FILESAVED])
-
Sadly, it doesn’t work for me. Folder c:\temp\mybackups exists. I’ve tried files on Z (mapped c:\xampp…), on С - nothing appeared in mybackups folder.
NPP 7.8 Windows 8.1
Is it possible to print debug info there? My primary language is PHP, so I’m not so smart here… -
Did you restart npp after creating your user startup.py?
Aprint(m)
before theif m
will print to the python script console.
Can be opened via the menu.
Just make sure you use the same indentation (tabs or spaces but not mixed) as the lines before and after. -
Did you notice this line in the demo script?:
top_level_backup_dir = r'c:\temp\mybackups'
Obviously you’d want to replace the path I used with the path you desire.
Otherwise I suggest the “print” route that @Ekopalypse recommended.
Saying “it doesn’t work” is fine but it doesn’t leave much room for help from someone not sitting right at your computer.
-
@teleslon2 said in Hierarchical backup wanted.:
Folder c:\temp\mybackups exists.
Oh, you did say this, so my comment about that doesn’t apply. Sorry.
-
That’s strange. I surely restarted it and not once. Ok, now it’s working (after inserting “print”, hmm…).
Next, I’d like to shorten some paths but my regex didn’t match, could you please help?print (backup_path) backup_path = re.sub(r"<c\:\\temp\\mybackups\\Drive\-C\\Users\\User\\AppData\\Local\\Temp\\scp\d+>", 'WinScp', backup_path)
Prints
c:\temp\mybackups\Drive-C\Users\User\AppData\Local\Temp\scp26772\home\admin\web
before replacing. -
Opps, there should by “c:\temp\mybackups\WinScp” as replacement string. But anyway, it prints the same after replacement.
-
Thanks, Alan!
And a guy helped me out there, so below is my final variant. I add time to file names and shorten paths derived from WinScp editing of remote files.from Npp import notepad, NOTIFICATION import os import shutil import re from datetime import datetime def callback_notepad_FILESAVED(args): curr_buffer_id = notepad.getCurrentBufferID() notepad.activateBufferID(args['bufferID']) p = notepad.getCurrentFilename() m = re.match('([C-Z]):', p, re.I) time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") if m: top_level_backup_dir = r'c:\temp\mybackups' backup_path = top_level_backup_dir + os.sep + 'Drive-{}'.format(m.group(1)) + p[2:] + '.' + time_now backup_path = re.sub(r"c\:\\temp\\mybackups\\Drive\-C\\Users\\User\\AppData\\Local\\Temp\\scp\d+", r'c:\\temp\\mybackups\\WinScp', backup_path) backup_dir = backup_path.rsplit(os.sep, 1)[0] if not os.path.isdir(backup_dir): os.makedirs(backup_dir) if os.path.isdir(backup_dir): shutil.copy2(p, backup_path) notepad.callback(callback_notepad_FILESAVED, [NOTIFICATION.FILESAVED])
-
I made for my environment
def callback_notepad_FILESAVED(args):
curr_buffer_id = notepad.getCurrentBufferID()
notepad.activateBufferID(args[‘bufferID’])
p = notepad.getCurrentFilename()
time_now = datetime.now().strftime(“%Y-%m-%d_%H-%M-%S”)
# print§
# m = re.match(‘([C-Z]):’, p, re.I)
if True:
# p1=p.replace(‘:’,‘/’).replace(os.sep,‘/’).replace(‘//’,‘/’)
# backup_path = top_level_backup_dir + os.sep + ‘Drive-{}’.format(m.group(1)) + p[2:]
# backup_path = top_level_backup_dir + os.sep + p1
# backup_dir = backup_path.rsplit(os.sep, 1)[0]
# if not os.path.isdir(backup_dir): os.makedirs(backup_dir)
# if os.path.isdir(backup_dir): shutil.copy2(p, backup_path)
top_level_backup_dir = ‘C:/temp/backup/’
p=p.replace(os.sep,‘/’)
pathLisr=p.split(‘/’)
pat=‘/’.join(pathLisr[:-1])
OriginalName=pathLisr[-1]
ext=os.path.splitext(OriginalName)[1]
new_name=os.path.splitext(OriginalName)[0]+time_now+ext
backup_dir=top_level_backup_dir + pat.replace(‘:’,‘/’).replace(os.sep,‘/’).replace(‘//’,‘/’)
new_name= backup_dir+‘/’+new_name
if not os.path.isdir(backup_dir): os.makedirs(backup_dir)
shutil.copyfile(p,new_name)
################################# -
Here is another version that backup file before it’s being overwritten with new data and appends date/time of the last modification date of the original file (not current date of the backup)
i.e. fileC:\blah\mycode.js
will be backed up asD:\Backup\Notepad++\C\blah\mycode.js_20210928_221824.js
It also works with network sharesfrom os import makedirs from os.path import split, splitext, exists, getmtime from shutil import copyfile from time import strftime, localtime from re import sub from Npp import * def callback_FILEBEFORESAVE(args): backup_dir = "D:\\Backup\\Notepad++\\"; # backup directory, must have trailing slash _file = notepad.getBufferFilename(args['bufferID']) # get full path if not exists(_file): # new file? return with open(_file, "r") as f: # read data from file if f.read() == editor.getText(): # only save if file was modified console.write(msg + "file unchanged, no backup created\n") return file = sub("^\\\\+", "", # remove \ at beginning of the path sub('[<>"|?*:]', "_", # just a precation, replace invalid characters with _ sub("^([a-zA-Z]):", r"\1", # remove : after drive letter _file))) dir, file = split(file) # get directory and file filename, ext = splitext(file) # get filename and extension new_dir = backup_dir + dir + "\\"; new_filename = filename + ext # original filename new_filename = new_filename + str(strftime("_%Y%m%d_%H%M%S", localtime(getmtime(_file)))) + ext; # append date/time and original extension if not exists(new_dir): try: makedirs(new_dir) except Exception as err: console.write(msg + str(err).replace("\\\\", "\\") + "\n") return try: copyfile(_file, new_dir + new_filename) console.write(msg + "saved as " + new_dir + new_filename + "\n") except Exception as err: console.write(msg + str(err).replace("\\\\", "\\") + "\n") notepad.clearCallbacks([NOTIFICATION.FILEBEFORESAVE]) notepad.callback(callback_FILEBEFORESAVE, [NOTIFICATION.FILEBEFORESAVE]) msg = "[Auto backup] " # console message prefix console.write(msg + "initialized\n")
For easy managing it’s better to save this script as a separate file i.e
autobackup.py
in the same directory wherestartup.py
is
and at the end ofstartup.py
add this line:import autobackup # script's filename without extension