Many of us miss an automatic recovery function within the Wolfram environment. After long notebook editing sessions, you can lose a lot of work when the system crashes, which happens occasionally. Wolfram’s NotebookAutoSave feature doesn’t really help. Based on grandrew's work published on StackExchange in 2017, I developed a solution that requires only one statement:
autosaveAllNbBackups[]
This needs to be run once for a Wolfram Desktop session, and then periodically saves backup copies of all open, modified notebooks. After a crash, these can be used to restore lost work. This notebook explains everything and contains the necessary code. It can be opened directly. At the very end you will find the statement to be executed once.

Contents

The Problem​​Solutions​ Auto-Save All Modified Notebooks In-Place by ScheduledTask​ grandrew’s solution​ Remarks​ My remarks and modifications of grandrew’s solution​ Remarks on Notebook Selection​ Remarks on Notebook Save​ Remarks on Scheduled Execution with RunScheduledTask​ Remarks on Using ScheduledTask and SessionSubmit or LocalSubmit​​ Periodically Auto-Save a Backup Copy of All Modified Notebooks​ My solution​ selectModifiedNbs - Select modified notebooks​ saveNbBackup - Save a notebook's backup copy​ saveAllNbBackups - Save backup copies of all modified notebooks​ autosaveAllNbBackups - Periodically Auto-Save Backup Copies of All Modified Notebooks​ Contains usage instructions

The Problem

How to automatically and regularly save all modified notebooks under different names than the original ones?
This way, in case of a crash, you have a notebook backup that is almost up to date.
​
The solution is derived here and then summarized in the Auto-Save section. It only takes one statement to achieve the desired goal (see at the very end of this notebook):
​
​
autosaveAllNbBackups[]
My solution is based on the work of grandrew published on StackExchange in 2017 (see below).

Notes

◼
  • The original notebooks will not be automatically saved, but only backup copies. The user still decides himself, which states of the original notebooks shall be saved.
  • ◼
  • Wolfram’s NotebookAutoSave is not suitable for this. It automatically saves the original after every generated part of the output. On the one hand, this is far too often, since running code without editing first does not need a save. On the other hand, it’s way too rare, since nothing is saved even after long editing sessions without executing anything.
  • Sources

    StackExchange

    Peeter Joot, Nov 02, 2012
    ​Is there a notebook autosave frequency configuration?
    I’ve had Mathematica crash on me a number of times, and have had to recreate notebook contents from the last save point.
    ​
    I was looking for something in the preferences like an autosave frequency configuration parameter that I could use to automatically save all my notebook content at some regular frequency to minimize losses due to crashes.
    ​
    I found NotebookSave[], and a NotebookAutoSave -> True attribute for CreateDocument, but both these appear to be …
    Nick Stranny, Jan 24, 2013
    ​Automatic recovery after crash
    I want Mathematica to periodically autosave my notebooks to the temp folder. And in case of front-end crash i want to see the dialog after restart with the following choices:
    - recover from autosaved drafts
    - discard drafts and open last manually saved versions
    - start a new session
    Ideally i would like to make a fully automated solution that doesn’t require to execute any commands each time i open a new notebook.
    …

    Solutions

    Auto-Save All Modified Notebooks In-Place by ScheduledTask

    grandrew’s solution
    This overwrites the original notebook(s) what is not desirable.
    grandrew, Jul 20, 2017
    ​https://mathematica.stackexchange.com/a/151883/79909
    For anybody looking for a simple solution you can add this at startup :
    RunScheduledTask[NotebookSave/@Select[Notebooks[],KeyExistsQ["Uri"]@NotebookInformation[#]&&("ModifiedInMemory"/.NotebookInformation[#])&],600];
    It saves every 600 seconds everything that has been saved at least once and has been modified in memory. This effectively ignores untitled windows, help, messages, etc. and saves whatever work you’ve been doing in every open notebook.

    Remarks

    My remarks and modifications of grandrew’s solution

    Remarks on Notebook Selection

    ◼
  • Notebooks[] gives all open notebooks:
  • ◼
  • Including new, unsaved notebooks, saved notebooks without explicit name, Messages window, help window:
  • In[]:=
    ClearAll["Global`*"]​​SetOptions[$FrontEnd,Language"English"]
    In[]:=
    Notebooks[]
    Out[]=
    NotebookObject
    AutoSave.nb
    ,NotebookObject
    SoundNote - Wolfram Desktop
    ,NotebookObject
    220116 Dominosteine.nb
    ,NotebookObject
    Untitled-1
    ,NotebookObject
    Untitled-3.nb
    ,NotebookObject
    230430 Passend zahlen.nb
    ,NotebookObject
    Messages
    
    ◼
  • NotebookInformation gives much, but not all information:
  • In[]:=
    NotebookInformation[]
    Out[]=
    FileNameFrontEnd`FileName[{$RootDirectory,C:,Users,Werner,OneDrive,WolframHome,Base,Hints,Recover},AutoSave.nb,CharacterEncodingUTF-8],FileModificationTime3.89236×
    9
    10
    ,WindowTitleAutoSave.nb,MemoryModificationTime3.89236×
    9
    10
    ,ModifiedInMemoryTrue,StorageSystemLocal,DocumentTypeNotebook,MIMETypeapplication/vnd.wolfram.nb,StyleDefinitionsNotebookObject
    788de132-9b65-4b1a-b2e9-ba25423e570a
    ,ExpressionUUIDf928c327-accb-46d9-9eda-b8854b9381d2
    ◼
  • Detecting notebooks with unsaved changes works fine with key “ModifiedInMemory”. But - strange enough - it shows the help notebook as modified.
  • In[]:=
    "ModifiedInMemory"/.NotebookInformation[]
    Out[]=
    True
    In[]:=
    Select[Notebooks[],("ModifiedInMemory"/.NotebookInformation[#])&]
    Out[]=
    NotebookObject
    AutoSave.nb
    ,NotebookObject
    SoundNote - Wolfram Desktop
    ,NotebookObject
    Untitled-1
    ,NotebookObject
    Untitled-3.nb
    ,NotebookObject
    Messages
    
    ◼
  • The key “Uri” normally does not exist within NotebookInformation. Hence KeyExistsQ[“Uri”] is useless.
  • In[]:=
    KeyExistsQ["Uri"]@NotebookInformation[]
    Out[]=
    False
    In[]:=
    Select[Notebooks[],KeyExistsQ["Uri"]@NotebookInformation[#]&]
    Out[]=
    {}
    ◼
  • The key “FileName” exists for all open notebooks except unsaved notebooks and Messages window. But It still includes the help notebooks:
  • In[]:=
    "FileName"/.NotebookInformation[]
    Out[]=
    FrontEnd`FileName[{$RootDirectory,C:,Users,Werner,OneDrive,WolframHome,Base,Hints,Recover},AutoSave.nb,CharacterEncodingUTF-8]
    In[]:=
    Select[Notebooks[],KeyExistsQ["FileName"]@NotebookInformation[#]&]
    Out[]=
    NotebookObject
    AutoSave.nb
    ,NotebookObject
    SoundNote - Wolfram Desktop
    ,NotebookObject
    220116 Dominosteine.nb
    ,NotebookObject
    Untitled-3.nb
    ,NotebookObject
    230430 Passend zahlen.nb
    
    ◼
  • But I don’t know how to get the full filename, i.e. the path, out of FrontEnd`FileName:
  • In[]:=
    "FileName"/.NotebookInformation[]
    Out[]=
    FrontEnd`FileName[{$RootDirectory,C:,Users,Werner,OneDrive,WolframHome,Base,Hints,Recover},AutoSave.nb,CharacterEncodingUTF-8]
    In[]:=
    Interpreter["FileName"]["FileName"/.NotebookInformation[]]
    Out[]=
    Failure
    
    Message:
    Enter a valid value.
    Tag:
    RestrictionFailure
    
    ◼
  • Fortunately we can use NotebookFileName to get the full filename, i.e. the path:
  • In[]:=
    NotebookFileName[]
    Out[]=
    C:\Users\Werner\OneDrive\WolframHome\Base\Hints\Recover\AutoSave.nb
    ◼
  • The key “DocumentType” can be used for separating help notebooks from user’s notebooks:
  • In[]:=
    "DocumentType"/.NotebookInformation[]
    Out[]=
    Notebook
    In[]:=
    Select[Notebooks[],TrueQ[("DocumentType"/.NotebookInformation[#])=="Notebook"]&]
    Out[]=
    NotebookObject
    AutoSave.nb
    ,NotebookObject
    220116 Dominosteine.nb
    ,NotebookObject
    Untitled-1
    ,NotebookObject
    Untitled-3.nb
    ,NotebookObject
    230430 Passend zahlen.nb
    ,NotebookObject
    Messages
    
    ◼
  • In summary, we find all notebooks to be backed up:
  • In[]:=
    Select[Notebooks[],​​KeyExistsQ["FileName"]@NotebookInformation[#]&&​​TrueQ[("DocumentType"/.NotebookInformation[#])=="Notebook"]&&​​("ModifiedInMemory"/.NotebookInformation[#])&]
    Out[]=
    NotebookObject
    AutoSave.nb
    ,NotebookObject
    Untitled-3.nb
    
    This selects all modified notebooks without notebooks that were never saved, the Messages window, help notebooks, etc.

    Remarks on Notebook Save

    ◼
  • We can build a modified name for the saved notebooks
    <directorypath>/<basefilename>.back.,<fileextension> by:
  • In[]:=
    NotebookFileName[]
    or:
    hence:
    ◼
  • We could save the notebook(s) under that name. But that would rename the original notebook and save it. We don’t want that.
  • ◼
  • We have to create a copy of the original notebook instead and save this copy under the new name.
  • ◼
  • To save all notebooks selected by the former code we use:
  • Note: Every notebook that has not been saved manually creates a backup copy again.

    Remarks on Scheduled Execution with RunScheduledTask

    ◼
  • Repeated code execution every 5 seconds is achieved by:
  • Note: The print output goes into the Messages window.
    RunScheduledTask returns a local ScheduledTask object which can be used to stop execution:
    Note: If the system crashes, glbAutoSaveSTO0 is lost and the task may continue to run. You must then terminate it via the Task Manager.
    ◼
  • A nicer notification sound can be played with:
  • i.e.:

    Remarks on Using ScheduledTask and SessionSubmit or LocalSubmit

    ◼
  • Use SessionSubmit, ScheduledTask, etc. since RunScheduledTask is being phased out by Wolfram.
  • As an example we use SessionSubmit which runs a task in the current session and has many features for better control of the background task. Here we use almost none of them.
    Since this task was running only once there is nothing running now:
    ◼
  • Specify and run a scheduled task within the current session executing every 5 seconds, leading a TaskObject:
  • We have the advantage to see the running tasks through Tasks[].
    Note: The print output goes into the Messages window.
    Show all running (local or session) tasks:
    Lookup the task’ UUID:
    Remove (terminate) task through TaskObject:
    Remove (terminate) task through UUID:
    Remove (terminate) all tasks:
    ◼
  • Use LocalSubmit, ScheduledTask.
  • As an example we use LocalSubmit which runs a task in a separate subkernel and has many features for better control of the background task. Here we use almost none of them.
    Since this task was running only once there is nothing running now:
    ◼
  • Specify and run a scheduled task within a separate subkernel executing every 5 seconds, leading a TaskObject:
  • We have the advantage that we can see the running tasks via Tasks[].
    ​
    In Task Manager and Running Tasks, this will show up as a second new Wolfram kernel. It is automatically terminated when the issuing Wolfram Desktop ends.
    Note: The print output does not go into the Messages window because there is no such window.
    Show all running (local or session) tasks:
    Remove (terminate) task or all tasks:
    Even now the new subkernel does not end. Here we have to kill it manually.
    ◼
  • In order to capture output from the local session we have to use HandlerFunctions. This is a bit complicated and not elaborated here. It has to be something like:
  • This is why we don’t use LocalSubmit here.
    Now we put it all together:

    Periodically Auto-Save a Backup Copy of All Modified Notebooks

    selectModifiedNbs - Select modified notebooks

    selectModifiedNbs[test:False] selects all open modified notebooks except those that were never saved. The message window, help notebooks, etc. are not selected.
    test produces echo output for testing.
    Returns a list of NotebookObject(s).

    autosaveAllNbBackups - Periodically Auto-Save Backup Copies of All Modified Notebooks

    - If you want to stop the backups, execute:
    After a crash:
    For all recently edited notebooks:
    - Open the original notebook (e.g. AutoSave.nb). This is the last manually saved state.
    - Open the backup copy (e.g. AutoSave.back.nb) and compare it with the original.
    - Manually copy pieces of code from the backup copy to the original.
    Or:
    - Just delete the original notebook and rename the backup copy to the original name.
    - Repeat from your memory all changes made (at most) during the last everySecs seconds.
    Show running Tasks:
    Stop execution through TaskObject or UUID:

    CITE THIS NOTEBOOK

    Auto-Save Notebooks & Recover After Crashes​
    by Werner Geiger​
    Wolfram Community, STAFF PICKS, May 8 2023
    ​https://community.wolfram.com/groups/-/m/t/2915671