Replace Strings
Walks a directory tree on the Clarive server and rewrites the files it finds, replacing ${var}
placeholders with values from the stash and optionally applying substitution
patterns of your own.
This is how most rules turn a checked-out source tree into something environment-specific: the
repository holds db.url=${db_url}, this op runs after the checkout, and the file that gets deployed
holds the real URL. To create a file from scratch instead of editing one, use
Write local file.
Fields¶
Path¶
Root of the tree to process, expanded from the stash. ${job_dir}/${project} is the usual value.
Leave it blank and the job stops on a missing-path message before anything else on the form is read.
It must be a directory that exists. Pointing it at a single file stops the job with a directory error, and pointing it at a path that is not there stops the job with an invalid path message.
The walk is always recursive and there is no way to limit the depth. Narrow the work with Includes
instead, or point Path deeper.
Slurp¶
Off by default.
Off, the file is read and rewritten one line at a time. On, the whole file is held in memory as one string and the patterns see it complete.
Slurp on is the only way for a pattern to match across a line break. It is also the only way to
destroy a file with a careless pattern: s{^.*$}{x} deletes one line per line with slurp off, and
the entire file with slurp on. Anchors change meaning too, since ^ and $ then refer to the start
and end of the file unless you add the m flag.
With slurp off, a ${var} placeholder split across two lines is not replaced.
Items Mode¶
| Choice | Which files are processed |
|---|---|
All files |
everything under Path that survives the filters |
Job Items |
only files whose path matches one of the current nature's item paths |
Default is All files.
Job Items needs a nature in scope, which means the op has to sit inside
APPLY NATURE. Outside that block there are no item paths to match
against, so nothing is processed and the job log reports zero changed files. Nothing warns you.
The item paths are used as regular expressions against the full path of each candidate, not compared as exact strings.
Output Dir¶
Blank by default, which means files are rewritten where they are.
Give it a directory and the originals are left alone: each processed file is written under this
directory, keeping its position relative to Path. Missing subdirectories are created.
The value is expanded from the stash like Path.
Suffix¶
Blank by default. Anything you type is appended to the output file name, so .tmp turns app.cfg
into app.cfg.tmp.
With no Output Dir, a suffix means the original file survives untouched and a second, processed
file appears next to it. That is a useful way to see what the substitution would do before you let it
run for real.
Patterns¶
A one-column grid with Add and Delete buttons. Add inserts a row pre-filled with s{}{}g;
click the row to edit it.
Each row is a Perl substitution expression applied to the text after variable expansion, in the order
the rows appear. The braces are delimiters, so s{foo}{bar}g replaces every foo with bar and
s{^#\s*}{} strips a leading comment marker. Flags go after the closing brace: g for every
occurrence, i for case-insensitive, m to make ^ and $ match at line boundaries inside a
slurped file.
An expression the engine cannot make sense of is skipped without stopping the job and without a message. A rule that appears to do nothing is nearly always a typo in one of these rows, and the fastest check is the changed-file count in the log.
The grid can stay empty. Variable expansion happens regardless, and most working configurations use no patterns at all.
Includes¶
Regular expressions, one row each, matched against the full path of every candidate file. A file is processed when it matches at least one row. An empty grid processes everything.
Add here pre-fills the new row with .*, which matches every path. Edit the row before you save,
or the include grid has no filtering effect at all.
Write \.properties$, not *.properties. Rows are patterns, not shell globs.
Leaving this empty means binary content gets rewritten too. There is no file type detection, so a jar
or a png under Path is read, variable-expanded and written back. Any ${ sequence that happens to
appear in the bytes is mangled. Put an include row in whenever the tree has anything other than text
in it.
Excludes¶
Same grid, same .* starting value on Add, applied after Includes. A file matching any row is
skipped no matter what the include rows said.
What the job log shows¶
Three entries. Sed: starting: with the path when the walk begins. A debug entry holding one line
per file explaining whether it was included, excluded or processed. Sed finished. Changed N file(s).
at the end, and when N is above zero a Sed changes entry listing each changed file with its
substitution count.
That count is the number that tells you whether the op did anything. A run that reports zero changed files did nothing, whatever it looked like it was configured to do.
Details worth knowing¶
Modification and access times are copied back onto the written file, so processing a tree does not make everything look freshly changed.
To leave a literal ${something} in a file rather than having it replaced, write $${something} in
the source; the extra dollar is dropped on the way out. A placeholder naming a variable that is not
in the stash is left as it is. ${var} is the only placeholder syntax expanded inside file contents,
so {{ ... }} passes through untouched even though other parts of a rule act on it.
With Slurp off, each file is rewritten through a temporary file next to the target and then renamed
over it, so an interrupted run leaves a stray file whose name ends in .bak. With Slurp on the
target is opened and emptied before anything is written, so an interrupted run leaves it truncated.
A file the walk reaches but cannot open for reading stops the job, and so does a file it cannot write back.
The op takes no backups and registers no rollback step, so a rollback does not restore the original contents. Check the tree out again when you need the untouched files.
Combining with other ops¶
The normal position is straight after a checkout:
Checkout Job Environment or
Checkout Job Items puts the source under ${job_dir}, this op fills in
the environment values, and Ship File Remotely sends the result.
Put the values it substitutes into the stash first, with SET VAR, from project variables, or from Load Job Items into Stash.
To see which files exist before committing to a run, use Load files/items into stash with the same include patterns.
For a single file with known contents, Write local file is simpler and safer than pointing this op at a directory containing one file.
Examples¶
Fill in environment placeholders across a checked-out project, text files only.
Path ${job_dir}/${project}
Slurp off
Items Mode All files
Output Dir (blank)
Suffix (blank)
Patterns (empty)
Includes \.(properties|xml|cfg)$
Excludes /target/
Rewrite one bundled asset in place, matched by name.
Path ${job_dir}/${project}/nodeserver/public/static/
Items Mode All files
Includes /main\..*\.js$
Patterns s{https://old-host}{https://${api_host}}g
Produce a processed copy elsewhere, leaving the checkout untouched, so a later step can compare the two trees.
Path ${job_dir}/myapp/conf
Output Dir ${job_dir}/myapp/conf-built
Includes \.tpl$
Patterns s{@@ENV@@}{${env}}g