File System - Find and Replace

Octopus.Script exported 2014-05-14 by bobjwalker belongs to ‘File System’ category.

Find and replace text in one or more files.

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Files

FRCandidatePathGlobs

The files to search. Wildcards * and ** are supported. Paths must be fully-qualified, e.g. C:\MyApp\**\*.xml. Separate multiple paths with ; semicolons.

Find

FRFindText

The text to find in the target files.

Replace with

FRReplaceText

The replacement text to insert in place of each occurrence of Find.

Ignore case

FRIgnoreCase = False

If True, variations on the character case of Find will be considered a match, for example Bar will match BAR and bar. If False only exact matches will be considered.

Script body

Steps based on this template will execute the following PowerShell script.

function Execute-FindReplace($target, $find, $replace, $ignoreCase) {
    $options = [System.Text.RegularExpressions.RegexOptions]::None
    if ($ignoreCase) {
        $options = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase
    }
    
    Write-Output "Searching $target..."
    $orig = [System.IO.File]::ReadAllText($target)
    
    $escFind = [System.Text.RegularExpressions.Regex]::Escape($find)
    $regex = new-object System.Text.RegularExpressions.Regex($escFind, $options)
    $removed = $regex.Replace($orig, '')
    
    $occurrences = ($orig.Length - $removed.Length) / $find.Length
    if ($occurrences -gt 0) {
        Write-Output "Found $occurrences occurrence(s), replacing..."
        
        $escReplace = $replace.Replace('$', '$$')
        $replaced = $regex.Replace($orig, $escReplace)
        [System.IO.File]::WriteAllText($target, $replaced)
    }
}

if ([string]::IsNullOrEmpty($FRFindText)) {
    throw "A non-empty 'Find' text block is required"
}

Write-Output "Replacing occurrences of '$FRFindText' with '$FRReplaceText'"
if ([Boolean] $FRIgnoreCase) {
    Write-Output "Case will be ignored"
}

$FRCandidatePathGlobs.Split(";") | foreach {
    $glob = $_.Trim()
    Write-Output "Searching for files that match $glob..."

    $matches = $null
    $splits = $glob.Split(@('/**/'), [System.StringSplitOptions]::RemoveEmptyEntries)

    if ($splits.Length -eq 1) {
        $splits = $glob.Split(@('\**\'), [System.StringSplitOptions]::RemoveEmptyEntries)
    }
    
    if ($splits.Length -eq 1) {
        $matches = ls $glob
    } else {
        if ($splits.Length -eq 2) {
            pushd $splits[0]
            $matches = ls $splits[1] -Recurse
            popd
        } else {
            $splits
            throw "The segment '**' can only appear once, as a directory name, in the glob expression"

        }
    }

    $matches | foreach {
        
        $target = $_.FullName

        Execute-FindReplace -target $target -find $FRFindText -replace $FRReplaceText -ignoreCase ([Boolean] $FRIgnoreCase)
    }
}


Write-Output "Done."

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "87cbaa94-4477-4474-a9c3-7943b5668d30",
  "Name": "File System - Find and Replace",
  "Description": "Find and replace text in one or more files.",
  "Version": 7,
  "ExportedAt": "2014-05-14T05:38:42.041+00:00",
  "ActionType": "Octopus.Script",
  "Author": "bobjwalker",
  "Parameters": [
    {
      "Name": "FRCandidatePathGlobs",
      "Label": "Files",
      "HelpText": "The files to search. Wildcards `*` and `**` are supported. Paths must be fully-qualified, e.g. `C:\\MyApp\\**\\*.xml`. Separate multiple paths with `;` semicolons.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "FRFindText",
      "Label": "Find",
      "HelpText": "The text to find in the target files.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "FRReplaceText",
      "Label": "Replace with",
      "HelpText": "The replacement text to insert in place of each occurrence of _Find_.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "FRIgnoreCase",
      "Label": "Ignore case",
      "HelpText": "If **True**, variations on the character case of _Find_ will be considered a match, for example `Bar` will match `BAR` and `bar`. If **False** only exact matches will be considered.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptBody": "function Execute-FindReplace($target, $find, $replace, $ignoreCase) {\n    $options = [System.Text.RegularExpressions.RegexOptions]::None\n    if ($ignoreCase) {\n        $options = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase\n    }\n    \n    Write-Output \"Searching $target...\"\n    $orig = [System.IO.File]::ReadAllText($target)\n    \n    $escFind = [System.Text.RegularExpressions.Regex]::Escape($find)\n    $regex = new-object System.Text.RegularExpressions.Regex($escFind, $options)\n    $removed = $regex.Replace($orig, '')\n    \n    $occurrences = ($orig.Length - $removed.Length) / $find.Length\n    if ($occurrences -gt 0) {\n        Write-Output \"Found $occurrences occurrence(s), replacing...\"\n        \n        $escReplace = $replace.Replace('$', '$$')\n        $replaced = $regex.Replace($orig, $escReplace)\n        [System.IO.File]::WriteAllText($target, $replaced)\n    }\n}\n\nif ([string]::IsNullOrEmpty($FRFindText)) {\n    throw \"A non-empty 'Find' text block is required\"\n}\n\nWrite-Output \"Replacing occurrences of '$FRFindText' with '$FRReplaceText'\"\nif ([Boolean] $FRIgnoreCase) {\n    Write-Output \"Case will be ignored\"\n}\n\n$FRCandidatePathGlobs.Split(\";\") | foreach {\n    $glob = $_.Trim()\n    Write-Output \"Searching for files that match $glob...\"\n\n    $matches = $null\n    $splits = $glob.Split(@('/**/'), [System.StringSplitOptions]::RemoveEmptyEntries)\n\n    if ($splits.Length -eq 1) {\n        $splits = $glob.Split(@('\\**\\'), [System.StringSplitOptions]::RemoveEmptyEntries)\n    }\n    \n    if ($splits.Length -eq 1) {\n        $matches = ls $glob\n    } else {\n        if ($splits.Length -eq 2) {\n            pushd $splits[0]\n            $matches = ls $splits[1] -Recurse\n            popd\n        } else {\n            $splits\n            throw \"The segment '**' can only appear once, as a directory name, in the glob expression\"\n\n        }\n    }\n\n    $matches | foreach {\n        \n        $target = $_.FullName\n\n        Execute-FindReplace -target $target -find $FRFindText -replace $FRReplaceText -ignoreCase ([Boolean] $FRIgnoreCase)\n    }\n}\n\n\nWrite-Output \"Done.\"",
    "Octopus.Action.Script.Syntax": "PowerShell"
  },
  "Category": "File System",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/file-system-find-and-replace.json",
  "Website": "/step-templates/87cbaa94-4477-4474-a9c3-7943b5668d30",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKhQTFRF/////78A/6oAVVVV/++//+q/gICA1dXV/79A/9R///ff/8MQ/9dg/8cg//vv/68A/+ef//PP//rv/7Ug/68Q/+Sv/8sw/9tw/9NQ/89w/+uv/+OP/8pg/89A/8VQ/9+f/9+A/9qP/+/P/99//7cA/7IA/7wA/6sA/+/A39/f/64A2tjX//Tfn5+f/7MA/7sA/74A/60A/9Zw/7gA/74Q/7oA/7UA/7EAi6g4fwAAAuRJREFUeNrs21tT2mAUheEvaaCQQCSczyi29WzP7f//Z6UdZBIIM44us9hf13vbC+eRbLo3qnNKKaWUUkoppZRSSimllFJKKaWUwlVrLhsvbrLqnoai2+yFr2x+fwKOwasZm/oXdEczxDQnOyYhqgn3uQpxDZhz3gdCesQ3r3mIjDfwXagj7NEgKywkXLMgSzCE9mz1wBDaOzDYETYEEUSQ/wPSaz6rQa174pDnt1x5Atm8emtPIGG48gUClJAh/XtPILi36+L3Zz6oVdBF4w32/sIbYmWH6qAPX5fzjio/14Q/W7nnqtIPDnYfFfThkGpPovXu68IhNdKSVyXkoQ7qgQypvwNVFwQDuXkP6oYM8WbYBWFDYMPNHnZBTv3tV8MuiCC+Q6K36+ypkn/LsJC4lQSkhhkQch4Qa+MgrYBahIKcB35AYrIjyEAQ9gsCm5E2GTIDQabsJysFQcZkxxj0H2LKfkGmIMjM3KgfgbBHfQSCROwnKwZByNtJsACt8WlChnRAkA7ZkaAOqyEZ0gJB7O2LRyAjsmOIutlNjnoJhD3qL9gXyyFjk6N+CKGPegSC2DsNj0DY++IlCEI/DWMQhL0vjh0GYvE0LIWwT8PEgSDsfXEEgmRmR30PYvI0LIGkRvfFAwj9NExBkIXRfXEfYvQ0PISwT8O2A0HYoz4DQb4ZPQ0PID8sj3oO8tXuvliEPJoe9Rzkk9XTcA9yZnhfLEA+mD0Ni5Ar9gvSAUE+2j0Ni5DPdk/DAuTW9L6Yg7BHfehAkC/WR30LoY96CoL8tL0v7iD0fTECQX4Z3xd3EPa+OANBflvfF58g363vi1sIfV+cgiCPPoz6Xwh71EcgyK0Xo76BsPfFBcbhrjzYF/9l/zTcxj4NWyBH7Mmo038zFvWCsCHt1HnxaCWZg8X8ifQwxjlcSvsJaHLpsGW5v2S8vo4qyymF7+5O3wOllFJKKaWUUkoppZRSSimllPKxPwIMAPj2YtijZbi5AAAAAElFTkSuQmCC",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Wednesday, May 14, 2014