Skip to content

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.

every file under Path Items Mode Includes Excludes ${...} expanded then Patterns line by line, or whole file rewritten in place copy under Output Dir no Output Dir Output Dir set always recursive

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