win_lineinfile backrefs bug fix and updated examples. (#20926)
* Bug Fix for win_lineinfile and updated examples. - changed $backrefs to a bool so it works with true/false/yes/no. This also fixes idempotency. - Updated Docs with an example of using backrefs. * Made suggested updates and converted two more parameters to "bool" * Updated the Exception message - Now contains the Windows Exception message as well as a custom message to help point in the right direction of a failed write. * Updated Exception Handling - Added Exception checks for Creating and removing the temporary files. - Changed the ErrorAction on the copy tmpfile and remove tmp file to "Stop" to cause the exception handler to catch all errors so we can fail gracefully every time.
This commit is contained in:
parent
52960ff7d0
commit
54de41309f
2 changed files with 67 additions and 43 deletions
|
@ -29,11 +29,11 @@ $path= Get-Attr $params "path" $FALSE;
|
|||
$regexp = Get-Attr $params "regexp" $FALSE;
|
||||
$state = Get-Attr $params "state" "present";
|
||||
$line = Get-Attr $params "line" $FALSE;
|
||||
$backrefs = Get-Attr $params "backrefs" "no";
|
||||
$backrefs = Get-Attr -obj $params -name "backrefs" -default "no" -type "bool"
|
||||
$insertafter = Get-Attr $params "insertafter" $FALSE;
|
||||
$insertbefore = Get-Attr $params "insertbefore" $FALSE;
|
||||
$create = Get-Attr $params "create" "no";
|
||||
$backup = Get-Attr $params "backup" "no";
|
||||
$create = Get-Attr $params -name "create" -default "no" -type "bool";
|
||||
$backup = Get-Attr $params -name "backup" -default "no" -type "bool";
|
||||
$validate = Get-Attr $params "validate" $FALSE;
|
||||
$encoding = Get-Attr $params "encoding" "auto";
|
||||
$newline = Get-Attr $params "newline" "windows";
|
||||
|
@ -63,30 +63,35 @@ If (Test-Path $path -pathType container) {
|
|||
}
|
||||
|
||||
|
||||
# Write lines to a file using the specified line separator and encoding,
|
||||
# Write lines to a file using the specified line separator and encoding,
|
||||
# performing validation if a validation command was specified.
|
||||
|
||||
function WriteLines($outlines, $path, $linesep, $encodingobj, $validate) {
|
||||
$temppath = [System.IO.Path]::GetTempFileName();
|
||||
Try {
|
||||
$temppath = [System.IO.Path]::GetTempFileName();
|
||||
}
|
||||
Catch {
|
||||
Fail-Json ("Cannot create temporary file! (" + $_.Exception.Message + ")")
|
||||
}
|
||||
$joined = $outlines -join $linesep;
|
||||
[System.IO.File]::WriteAllText($temppath, $joined, $encodingobj);
|
||||
|
||||
|
||||
If ($validate -ne $FALSE) {
|
||||
|
||||
|
||||
If (!($validate -like "*%s*")) {
|
||||
Fail-Json (New-Object psobject) "validate must contain %s: $validate";
|
||||
}
|
||||
|
||||
$validate = $validate.Replace("%s", $temppath);
|
||||
|
||||
$validate = $validate.Replace("%s", $temppath);
|
||||
|
||||
$parts = [System.Collections.ArrayList] $validate.Split(" ");
|
||||
$cmdname = $parts[0];
|
||||
|
||||
|
||||
$cmdargs = $validate.Substring($cmdname.Length + 1);
|
||||
|
||||
|
||||
$process = [Diagnostics.Process]::Start($cmdname, $cmdargs);
|
||||
$process.WaitForExit();
|
||||
|
||||
|
||||
If ($process.ExitCode -ne 0) {
|
||||
[string] $output = $process.StandardOutput.ReadToEnd();
|
||||
[string] $error = $process.StandardError.ReadToEnd();
|
||||
|
@ -95,11 +100,23 @@ function WriteLines($outlines, $path, $linesep, $encodingobj, $validate) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Commit changes to the path
|
||||
$cleanpath = $path.Replace("/", "\");
|
||||
Copy-Item $temppath $cleanpath -force;
|
||||
Remove-Item $temppath -force;
|
||||
Try {
|
||||
Copy-Item $temppath $cleanpath -force -ErrorAction Stop;
|
||||
}
|
||||
Catch {
|
||||
Fail-Json ("Cannot write to: $cleanpath (" + $_.Exception.Message + ")")
|
||||
}
|
||||
|
||||
Try {
|
||||
Remove-Item $temppath -force -ErrorAction Stop;
|
||||
}
|
||||
Catch {
|
||||
Fail-Json ("Cannot remove temporary file: $temppath (" + $_.Exception.Message + ")")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -117,14 +134,14 @@ function BackupFile($path) {
|
|||
|
||||
function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $backup, $backrefs, $validate, $encodingobj, $linesep) {
|
||||
|
||||
# Note that we have to clean up the path because ansible wants to treat / and \ as
|
||||
# Note that we have to clean up the path because ansible wants to treat / and \ as
|
||||
# interchangeable in windows pathnames, but .NET framework internals do not support that.
|
||||
$cleanpath = $path.Replace("/", "\");
|
||||
|
||||
# Check if path exists. If it does not exist, either create it if create == "yes"
|
||||
# was specified or fail with a reasonable error message.
|
||||
If (!(Test-Path $path)) {
|
||||
If ($create -eq "no") {
|
||||
If (-not $create) {
|
||||
Fail-Json (New-Object psobject) "Path $path does not exist !";
|
||||
}
|
||||
# Create new empty file, using the specified encoding to write correct BOM
|
||||
|
@ -139,16 +156,16 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
Else {
|
||||
$lines = [System.Collections.ArrayList] $content;
|
||||
}
|
||||
|
||||
|
||||
# Compile the regex specified, if provided
|
||||
$mre = $FALSE;
|
||||
If ($regexp -ne $FALSE) {
|
||||
$mre = New-Object Regex $regexp, 'Compiled';
|
||||
}
|
||||
|
||||
|
||||
# Compile the regex for insertafter or insertbefore, if provided
|
||||
$insre = $FALSE;
|
||||
|
||||
|
||||
If ($insertafter -ne $FALSE -and $insertafter -ne "BOF" -and $insertafter -ne "EOF") {
|
||||
$insre = New-Object Regex $insertafter, 'Compiled';
|
||||
}
|
||||
|
@ -160,7 +177,7 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
# index[1] is the line num where insertafter/inserbefore has been found
|
||||
$index = -1, -1;
|
||||
$lineno = 0;
|
||||
|
||||
|
||||
# The latest match object and matched line
|
||||
$matched_line = "";
|
||||
$m = $FALSE;
|
||||
|
@ -186,16 +203,16 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
}
|
||||
If ($insertbefore -ne $FALSE) {
|
||||
$index[1] = $lineno;
|
||||
}
|
||||
}
|
||||
}
|
||||
$lineno = $lineno + 1;
|
||||
}
|
||||
|
||||
|
||||
$changed = $FALSE;
|
||||
$msg = "";
|
||||
|
||||
If ($index[0] -ne -1) {
|
||||
If ($backrefs -ne "no") {
|
||||
If ($backrefs) {
|
||||
$new_line = [regex]::Replace($matched_line, $regexp, $line);
|
||||
}
|
||||
Else {
|
||||
|
@ -207,7 +224,7 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
$changed = $TRUE;
|
||||
}
|
||||
}
|
||||
ElseIf ($backrefs -ne "no") {
|
||||
ElseIf ($backrefs) {
|
||||
# No matches - no-op
|
||||
}
|
||||
ElseIf ($insertbefore -eq "BOF" -or $insertafter -eq "BOF") {
|
||||
|
@ -229,10 +246,10 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
# Write backup file if backup == "yes"
|
||||
$backuppath = "";
|
||||
|
||||
If ($changed -eq $TRUE -and $backup -eq "yes") {
|
||||
If ($changed -eq $TRUE -and $backup -eq $TRUE) {
|
||||
$backuppath = BackupFile $path;
|
||||
}
|
||||
|
||||
|
||||
# Write changes to the path if changes were made
|
||||
If ($changed) {
|
||||
WriteLines $lines $path $linesep $encodingobj $validate;
|
||||
|
@ -247,7 +264,7 @@ function Present($path, $regexp, $line, $insertafter, $insertbefore, $create, $b
|
|||
backup = $backuppath
|
||||
encoding = $encodingstr
|
||||
}
|
||||
|
||||
|
||||
Exit-Json $result;
|
||||
}
|
||||
|
||||
|
@ -262,9 +279,9 @@ function Absent($path, $regexp, $line, $backup, $validate, $encodingobj, $linese
|
|||
}
|
||||
|
||||
# Read the dest file lines using the indicated encoding into a mutable ArrayList. Note
|
||||
# that we have to clean up the path because ansible wants to treat / and \ as
|
||||
# that we have to clean up the path because ansible wants to treat / and \ as
|
||||
# interchangeable in windows pathnames, but .NET framework internals do not support that.
|
||||
|
||||
|
||||
$cleanpath = $path.Replace("/", "\");
|
||||
$content = [System.IO.File]::ReadAllLines($cleanpath, $encodingobj);
|
||||
If ($content -eq $null) {
|
||||
|
@ -273,7 +290,7 @@ function Absent($path, $regexp, $line, $backup, $validate, $encodingobj, $linese
|
|||
Else {
|
||||
$lines = [System.Collections.ArrayList] $content;
|
||||
}
|
||||
|
||||
|
||||
# Initialize message to be returned on success
|
||||
$msg = "";
|
||||
|
||||
|
@ -307,10 +324,10 @@ function Absent($path, $regexp, $line, $backup, $validate, $encodingobj, $linese
|
|||
# Write backup file if backup == "yes"
|
||||
$backuppath = "";
|
||||
|
||||
If ($changed -eq $TRUE -and $backup -eq "yes") {
|
||||
If ($changed -eq $TRUE -and $backup -eq $TRUE) {
|
||||
$backuppath = BackupFile $path;
|
||||
}
|
||||
|
||||
|
||||
# Write changes to the path if changes were made
|
||||
If ($changed) {
|
||||
WriteLines $left $path $linesep $encodingobj $validate;
|
||||
|
@ -328,7 +345,7 @@ function Absent($path, $regexp, $line, $backup, $validate, $encodingobj, $linese
|
|||
found = $fcount
|
||||
encoding = $encodingstr
|
||||
}
|
||||
|
||||
|
||||
Exit-Json $result;
|
||||
}
|
||||
|
||||
|
@ -362,7 +379,7 @@ If ($encoding -ne "auto") {
|
|||
}
|
||||
|
||||
# Otherwise see if we can determine the current encoding of the target file.
|
||||
# If the file doesn't exist yet (create == 'yes') we use the default or
|
||||
# If the file doesn't exist yet (create == 'yes') we use the default or
|
||||
# explicitly specified encoding set above.
|
||||
Elseif (Test-Path $path) {
|
||||
|
||||
|
@ -382,11 +399,11 @@ Elseif (Test-Path $path) {
|
|||
}
|
||||
|
||||
# Get the first N bytes from the file, where N is the max preamble length we saw
|
||||
|
||||
|
||||
[Byte[]]$bom = Get-Content -Encoding Byte -ReadCount $max_preamble_len -TotalCount $max_preamble_len -Path $path;
|
||||
|
||||
|
||||
# Iterate through the sorted encodings, looking for a full match.
|
||||
|
||||
|
||||
$found = $FALSE;
|
||||
Foreach ($encoding in $sortedlist.GetValueList()) {
|
||||
$preamble = $encoding.GetPreamble();
|
||||
|
@ -411,19 +428,19 @@ Elseif (Test-Path $path) {
|
|||
}
|
||||
|
||||
|
||||
# Main dispatch - based on the value of 'state', perform argument validation and
|
||||
# Main dispatch - based on the value of 'state', perform argument validation and
|
||||
# call the appropriate handler function.
|
||||
|
||||
If ($state -eq "present") {
|
||||
|
||||
If ( $backrefs -ne "no" -and $regexp -eq $FALSE ) {
|
||||
If ( $backrefs -and $regexp -eq $FALSE ) {
|
||||
Fail-Json (New-Object psobject) "regexp= is required with backrefs=true";
|
||||
}
|
||||
|
||||
|
||||
If ($line -eq $FALSE) {
|
||||
Fail-Json (New-Object psobject) "line= is required with state=present";
|
||||
}
|
||||
|
||||
|
||||
If ($insertbefore -eq $FALSE -and $insertafter -eq $FALSE) {
|
||||
$insertafter = "EOF";
|
||||
}
|
||||
|
@ -436,6 +453,6 @@ Else {
|
|||
If ($regexp -eq $FALSE -and $line -eq $FALSE) {
|
||||
Fail-Json (New-Object psobject) "one of line= or regexp= is required with state=absent";
|
||||
}
|
||||
|
||||
|
||||
Absent $path $regexp $line $backup $validate $encodingobj $linesep;
|
||||
}
|
||||
|
|
|
@ -147,4 +147,11 @@ EXAMPLES = r'''
|
|||
path: C:\temp\testfile.txt
|
||||
line: Line added to file
|
||||
newline: unix
|
||||
|
||||
# Update a line using backrefs
|
||||
- win_lineinfile:
|
||||
path: C:\temp\example.conf
|
||||
backrefs: yes
|
||||
regexp: '(^name=)'
|
||||
line: '$1JohnDoe'
|
||||
'''
|
||||
|
|
Loading…
Reference in a new issue