Search code examples
matlabmatlab-java

Run particular cell section from command line in Matlab?


I am cycling through various cells in Matlab by hand in a script (let's call it foo.m):

%%
%Code for cell 1

%%
%Code for cell 2

From the command line of Matlab, I would like to be able to selectively run the code in cell 2. The documentation only has instructions for how to do it interactively (e.g., place the cursor in the appropriate cell, then blah blah). I would like something for the command line so I can do something like foo.runCell(1) to run the code in cell 1 above.

If there is no way to do it, I will just split the cells up into separate scripts/functions. This is less convenient, as I am in a 'grind out prototype very quickly' mode of coding, so want everything in one file for now.


Solution

  • Dev-iL has provided a good answer using java etc... I will provide an alternative here which does not use java or the editor, but reads the file and evals the statements when requested.

    For this to work it has a pre-requirement that the file has been saved (which I appreciate is not a pre-requisite of running cells/codeBlocks interactively).

    Anyway I thought this was a "quirky" question and thought I'd add have a go at an answer.

    Here is my script (cellScript.m) which contains code blocks (Note I have given each block a name after the "%%":

    %Note: for this method your NOT allowed to put comments at the end of lines
    %% cellA
    disp ( 'running cell A' ); 
    disp ( 'finished cell A' );
    %% cellB
    disp ( 'running cell B' );
    disp ( 'finished cell B' );
    %% cellC
    disp ( 'running cell C' );
    for ii=1:100
      aa(ii) = randi(1);
    end
    % cells can have comments
    disp ( 'past 1st coment' );
       % cells comments can be indented
    disp ( 'past indented comment' );
    
      %{
       block 
       comment
      %}
    disp ( 'past block comment' );
    
    % cells can have comments
    % multiple lines of comments
      % not lined up
    %
    %and empty
    
    disp ( 'past multiple lines of comments' );
    
    disp ( 'finished cell C' );
    %% cellD
    disp ( 'running cell D' );
    
    disp ( 'finished cell D' );
    %% cellE
    disp ( 'running cell E' );
    disp ( 'finished cell E' );
    

    I have created a class which does the job requested (I called it cellRunner.m )

    classdef cellRunner < handle
      properties ( SetAccess = private )
        fileName
        fileInfo
        cellInfo
        cellNames = {};
      end
      methods 
        function obj = cellRunner ( file ) % constructor
          if nargin == 0                        
            obj.fileName = 'cellScript.m';      % default file for testing
          else
            obj.fileName = file;                % store user file
          end
          obj.parseFile();                      % read the file into memory
        end
        function obj = parseFile ( obj )
          if ~isempty ( obj.fileInfo )                        % on parsing check to see if its been parsed before
            if isequal ( obj.fileInfo, dir ( obj.fileName ) ) % Check date stamp (has cell file been modified
    %           disp ( 'file not changed - reading skipped' );  % if not skip
    %           reading 
              return
            end
          end
          obj.fileInfo = dir ( obj.fileName );                % store file info
          fid = fopen ( obj.fileName );                       % open file for reading
          if fid ~= -1
            index = 0;                                        % this is the index of each cell
            inCell = false;                                   % has it found a cell to start reading
            lines = cell(0);                                  
            while ( true )
              line = fgetl ( fid );                           % read the line in the file
              if line == -1; break; end                       % check for the end of the file
              sLine = strtrim ( line );                       % trim any white space
              if length ( sLine ) > 2 && strcmp ( sLine(1:2), '%%' ) % check to see if its the start of a cell
                if index > 0                                  % Store the last cell data                
                  obj.cellInfo{index} = lines;                % in class to run when required
                end
                index = index + 1;                            % increment the index
                obj.cellNames{index} = strtrim ( sLine(3:end) ); % save the name of the cell
                lines = cell(0);                              % re-initialise the lines var
                inCell = true;                                % the start of the cells have been found
              elseif inCell                                   % if reading a cell array
                lines{end+1} = line;                          % add each line to the lines var
              end          
            end
            if index > 0                                      % make sure and save the last cell when finished reading
              obj.cellInfo{index} = lines;
            end
            fclose ( fid );
          else
            error ( 'cellRunner:fileError', 'unable to read file' );
          end
        end
        function obj = runCell ( obj, arg )
          % obj.runCell ( 'cellName' );
          % obj.runCell ( index );
          obj.parseFile();                                    % check that the file hasn't been changed
          if ischar ( arg )                                   % if user provided a char then search for it
            index = strcmp ( arg, obj.cellNames );            % find the index
            if ~any ( index )                                 % check it was found
              error ( 'cellRunner:notFound', '%s not found', arg ); 
            end
          else
            index = arg;                                      % if index is an integer (not checked - assumed if not char)
            if index < 1 || index > length ( obj.cellInfo )   % check integer is valid
              error ( 'cellRunner:notFound', 'Index %d not found', arg );
            end
          end
          commands = obj.cellInfo{index}{1};                  % start to build the command to execute.
          inBlock = false;
          for ii=2:length(obj.cellInfo{index})                % loop around - ignoring any commented lines.
            nextLine = strtrim ( obj.cellInfo{index}{ii} ); 
            if inBlock
              if length ( nextLine ) == 2 && strcmp ( nextLine, '%}' );
                inBlock = false;
              end
              continue
            end
            if length ( nextLine ) == 2 && strcmp ( nextLine, '%{' );
              inBlock = true;
              continue
            end
            if length ( nextLine ) >= 1 && strcmp ( nextLine(1), '%' )
              continue;
            end
            commands = sprintf ( '%s;%s', commands, obj.cellInfo{index}{ii} ); % build a parge string to eval
          end
          evalin('base',commands);                            % eval the expression in the base workspace.
        end
      end
    end
    

    The code is then used as follows:

    obj.cellRunner();
    % Individual cells can be run in two ways:
    
    % By providing the name of the cell (the string after the %%)
    obj.runCell ( 'cellC' );
    % By providing the index
    obj.runCell ( 3 );
    

    Note Recall the file must be saved for this to work.

    Sample run:

    whos
    obj = cellRunner ( 'cellScript.m' );
    obj.runCell ( 'cellC' );
    running cell C
    past 1st coment
    past indented comment
    past block comment
    past multiple lines of comments
    finished cell C
    whos
      Name      Size             Bytes  Class         Attributes
    
      aa        1x100              800  double                  
      ans       1x1                112  cellRunner              
      ii        1x1                  8  double                  
      obj       1x1                112  cellRunner              
    

    Note 1 - Why handle class? I inherit from the handle class because I only want one copy of my file data that has been read - see answer 1 in this question for an excellent overview of when to use value/handle classes.