AppyPrinciplesGetting聽started
appy.pod Command-line programs ogrep 路 Find & replace within POD templates

After years of experiencing countless success stories using Appy on various projects, you may end up with a plethora of POD templates. Consequently, these needs may emerge:

  • searching keywords within POD templates ;
  • performing find / replace operations within your templates.

This what ogrep.py is all about.

ogrep stands for "OpenDocument Grep" and is meant to perform grep-like searches within a given POD template or within all POD templates being found, recursively, in a given folder. Although it is possible, with ogrep, to grep any part of an OpenDocument template (be it an ODT or ODS template), the main focus is on searching and/or replacing keywords within POD-specific zones, ie, comments (notes) and fields.

Searching for keywords

Suppose you have this template, named Agenda.py, in the current working directory. A part of this template could look like this:

If you want to find all usages of method getShownValue in your template, you may use ogrep.py this way:

appy ogrep getShownValue Agenda.odt

The result would be:

pod/Agenda.odt matches 1 time(s).

Searching for keyword item would match more results:

appy ogrep item Agenda.odt
pod/Agenda.odt matches 5 time(s).

If you want more output, use options -v or -vv:

appy ogrep getShownValue Agenda.odt -v
:: 1 match for Agenda.odt ::
  Content:
item.proposingGroup.getShownValue()

With even more verbosity, you get this result.

appy ogrep getShownValue Agenda.odt -vv
:: 1 match for Agenda.odt ::
  Match::on::field (tag text:conditional-text)
  Keyword(s) (regex): getShownValue 
  Content:
item.proposingGroup.getShownValue()

All the examples so far illustrated keywords found within fields. The following example matches a keyword in a comment. Template Agenda.py contains these comments:

Performing the following ogrep produces this result:

appy ogrep is.mpty Agenda.odt -vv
 
:: 6 matches for Agenda.odt ::
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:05:06
 
  Content:
 
do text if not tool.isEmpty('logo')
from document(at=tool.path('logo'))
聽
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:10:58
  
  Content:
聽
do text if not tool.isEmpty('organization')

...

This example illustrates another element: the keyword is, by default, interpreted as a regular expression. The dot matching anything, is.mpty matches isEmpty, found 6 times in the POD template (the result was truncated to the 2 first results for conciseness).

If you want the keyword to be interpreted as a strict string, use option -s:

appy ogrep is.mpty Agenda.odt -vv -s
No match found.

The hereabove examples were about searching for keywords within a single POD template. It is also possible to search (and replace, see below) for keywords, recursively, within all POD templates found in a folder hierarchy, lie in this example.

appy ogrep "search" ../../HubSessions
../../HubSessions/pod/Revenues.ods matches 1 time(s).
../../HubSessions/pod/Tasks.ods matches 2 time(s).
../../HubSessions/pod/sub/Items.ods matches 2 time(s).
../../HubSessions/pod/Items.odt matches 3 time(s).
../../HubSessions/Meetings.odt matches 3 time(s).
../../HubSessions/pod/a/b/c/Tasks.odt matches 3 time(s).

Also note that ogrep works fine with ODT as well as ODS POD templates.

Performing replacements

Performing a find / replace with ogrep is as simple as adding option -r repl to an ogrep command. Before performing such a delicate operation, a dry run mode has been implemented via option -d. Suppose you want to replace string search_title with the more elegant camelCased searchTitle. You may test it with this command:

appy ogrep search_title ../../HubSessions -r searchTitle -vv -d

Every match would look like this:

:: 1 match for ../../HubSessions/pod/Revenues.ods ::
 
  Match::on::field (tag table:table-cell)
  Keyword(s) (regex): search_title  
  These changes would have been done:
 
- '%s 鈥 Extrait du %s' % (search.translated or _('search_title'), d(now, withHour=False))
?                                                       ^^
 
+ '%s 鈥 Extrait du %s' % (search.translated or _('searchTitle'), d(now, withHour=False))
?                                                       ^

By default, differences between the POD zones before or after having performed the replacement are outputed using the standard diff syntax. If you prefer colors, use the -n option:

appy ogrep search_title ../../HubSessions -r searchTitle -vv -d -n

Here is the result:

In order to perform the true replacement, simply remove the -d option.

Reference

All program options are documented hereafter.

usage: ogrep.py [-h] [-r REPL] [-v] [-vv] [-d] [-c] [-s] [-n] keyword path
聽
positional arguments:
聽 keyword 聽 聽 聽 聽 聽 聽 聽 is the regular expression (or string if -s) to search
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 within the file(s). From the command-line, special
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 chars possibly included in the keyword (and also the
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 replacement string, if passed) must be escaped. For
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 example, cus(tom)_(agenda) must be written as
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 cus\(tom\)_\(agenda\). \2_\1 must be written as
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 \\2_\\1.
聽 path 聽 聽 聽 聽 聽 聽 聽 聽 聽is the path to a file or folder. If a file is passed,
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 you must pass the path to an ODF file (odt or ods).
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 ogrep will be run on this file only. If a folder is
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 passed, ogrep will be run on all ODF files found in
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 this folder and sub-folders.
聽
optional arguments:
聽 -h, --help 聽 聽 聽 聽 聽 聽show this help message and exit
聽 -r REPL, --repl REPL 聽if this replacement string is passed, within matched
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 ODF files, the keyword will be replaced with it. The
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 replacement string may contain references to groups
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 possibly defined within the "keyword" regular
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 expression, but only via the "\<nb>" notation.
聽 -v, --verbose 聽 聽 聽 聽 dump more info about the find/replace zones (only
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 available if option "-c" is not used).
聽 -vv 聽 聽 聽 聽 聽 聽 聽 聽 聽 similar to -v, must with even more *v*erbosity.
聽 -d, --dry-run 聽 聽 聽 聽 in the context of a replacement, if this option is
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 set, no replacement will actually be performed, but
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 output will give info (detailed if -v, minimal if not)
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 about what replacements would have been performed
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 without this option.
聽 -c, --in-content 聽 聽 聽if unset, the find/replace process will only apply to
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 the POD-controlled zones of POD template(s): notes and
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 fields. If the option is set, it will occur in any
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 part of the template(s).
聽 -s, --as-string 聽 聽 聽 by default, keyword is interpreted as a regular
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 expression. If you want it to be a plain *s*tring
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 instead, set this option.
聽 -n, --nice 聽 聽 聽 聽 聽 聽if set in conjunction with -v or -vv, changes (being
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 effective or potential, depending on -d) will be
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 colorized. Else, changes will be rendered as standard
聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 diff output.